2 * Copyright (c) 2021 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-parameters.h>
43 #if defined(DEBUG_ENABLED)
44 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
47 const float MAX_FLOAT = std::numeric_limits<float>::max();
48 const CharacterDirection LTR = false;
49 const CharacterDirection RTL = !LTR;
50 const float LINE_SPACING = 0.f;
51 const float MIN_LINE_SIZE = 0.f;
52 const Character HYPHEN_UNICODE = 0x002D;
54 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
56 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
62 * @brief Stores temporary layout info of the line.
70 numberOfCharacters{0u},
77 whiteSpaceLengthEndOfLine{0.f},
79 isSplitToTwoHalves(false),
80 glyphIndexInSecondHalfLine{0u},
81 characterIndexInSecondHalfLine{0u},
82 numberOfGlyphsInSecondHalfLine{0u},
83 numberOfCharactersInSecondHalfLine{0u}
97 numberOfCharacters = 0u;
98 ascender = -MAX_FLOAT;
99 descender = MAX_FLOAT;
101 isSplitToTwoHalves = false;
102 glyphIndexInSecondHalfLine = 0u;
103 characterIndexInSecondHalfLine = 0u;
104 numberOfGlyphsInSecondHalfLine = 0u;
105 numberOfCharactersInSecondHalfLine = 0u;
108 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
109 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
110 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
111 Length numberOfCharacters; ///< The number of characters which fit in one line.
112 float ascender; ///< The maximum ascender of all fonts in the line.
113 float descender; ///< The minimum descender of all fonts in the line.
114 float lineSpacing; ///< The line spacing
115 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
116 float previousAdvance; ///< The advance of the previous glyph.
117 float length; ///< The current length of the line.
118 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
119 CharacterDirection direction;
121 bool isSplitToTwoHalves; ///< Whether the second half is defined.
122 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
123 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
124 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
125 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
128 struct LayoutBidiParameters
132 paragraphDirection = LTR;
133 bidiParagraphIndex = 0u;
135 isBidirectional = false;
138 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
139 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
140 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
141 bool isBidirectional = false; ///< Whether the text is bidirectional.
147 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
149 mDefaultLineSpacing{LINE_SPACING},
150 mDefaultLineSize{MIN_LINE_SIZE}
155 * @brief Updates the line ascender and descender with the metrics of a new font.
157 * @param[in] glyphMetrics The metrics of the new font.
158 * @param[in,out] lineLayout The line layout.
160 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
162 Text::FontMetrics fontMetrics;
163 if(0u != glyphMetrics.fontId)
165 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
169 fontMetrics.ascender = glyphMetrics.fontHeight;
170 fontMetrics.descender = 0.f;
171 fontMetrics.height = fontMetrics.ascender;
172 fontMetrics.underlinePosition = 0.f;
173 fontMetrics.underlineThickness = 1.f;
176 // Sets the maximum ascender.
177 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
179 // Sets the minimum descender.
180 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
182 // Sets the line size
183 lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
184 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
186 // Add the line spacing
187 lineLayout.lineSpacing += mDefaultLineSpacing;
191 * @brief Merges a temporary line layout into the line layout.
193 * @param[in,out] lineLayout The line layout.
194 * @param[in] tmpLineLayout A temporary line layout.
195 * @param[in] isShifted Whether to shift first glyph and character indices.
197 void MergeLineLayout(LineLayout& lineLayout,
198 const LineLayout& tmpLineLayout,
201 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
202 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
204 lineLayout.penX = tmpLineLayout.penX;
205 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
207 lineLayout.length = tmpLineLayout.length;
208 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
210 // Sets the maximum ascender.
211 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
213 // Sets the minimum descender.
214 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
216 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
219 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
220 lineLayout.characterIndex = tmpLineLayout.characterIndex;
223 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
224 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
225 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
226 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
227 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
230 void LayoutRightToLeft(const Parameters& parameters,
231 const BidirectionalLineInfoRun& bidirectionalLineInfo,
233 float& whiteSpaceLengthEndOfLine)
235 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
236 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
237 // then the first half of line "characterRun",
238 // Otherwise travers whole characters in"characterRun".
240 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
241 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
242 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
243 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
245 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
246 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
248 CharacterIndex characterLogicalIndex = 0u;
249 CharacterIndex characterVisualIndex = 0u;
251 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
252 // Otherwise maps the first visual index from visualToLogicalMap.
253 // This is to initialize the first visual index.
254 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
256 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
260 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
263 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
265 if(RTL == bidirectionalLineInfo.direction)
267 // If there are characters in the second half of Line.
268 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
270 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
271 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
273 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
275 whiteSpaceLengthEndOfLine += glyphInfo.advance;
277 ++characterLogicalIndex;
278 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
282 // If all characters in the second half of Line are WhiteSpaces.
283 // then continue adding the WhiteSpaces from the first hel of Line.
284 // Also this is valid when the line was not splitted.
285 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
287 extendedToSecondHalf = true; // Whether the logical index is extended to second half
288 characterLogicalIndex = 0u;
289 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
291 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
292 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
294 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
296 whiteSpaceLengthEndOfLine += glyphInfo.advance;
298 ++characterLogicalIndex;
299 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
304 // Here's the first index of character is not WhiteSpace
305 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
307 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
308 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
309 lastGlyphOfParagraphPlusOne,
310 charactersPerGlyphBuffer);
312 GlyphMetrics glyphMetrics;
313 GetGlyphsMetrics(glyphIndex,
314 numberOfGLyphsInGroup,
319 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
321 // Traverses the characters of the right to left paragraph.
322 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
323 if(!extendedToSecondHalf &&
324 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
326 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
328 // Convert the character in the logical order into the character in the visual order.
329 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
330 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
332 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
334 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
335 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
336 lastGlyphOfParagraphPlusOne,
337 charactersPerGlyphBuffer);
339 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
341 GlyphMetrics glyphMetrics;
342 GetGlyphsMetrics(glyphIndex,
343 numberOfGLyphsInGroup,
350 // If glyph is WhiteSpace then:
351 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
352 // the endOfLine in RTL was the headOfLine for layouting.
353 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
354 if(RTL == bidirectionalLineInfo.direction)
356 length += glyphMetrics.advance;
360 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
362 penX += glyphMetrics.advance;
366 // If glyph is not whiteSpace then:
367 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
368 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
369 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
370 // Otherwise the current length is maximum.
371 if(LTR == bidirectionalLineInfo.direction)
373 whiteSpaceLengthEndOfLine = 0.f;
375 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
376 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
381 // Continue traversing in the first half of line or in the whole line.
382 // If the second half of line was extended then continue from logical index in the first half of line
383 // Also this is valid when the line was not splitted and there were WhiteSpace.
384 // Otherwise start from first logical index in line.
385 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
386 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
388 // Convert the character in the logical order into the character in the visual order.
389 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
390 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
392 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
394 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
395 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
396 lastGlyphOfParagraphPlusOne,
397 charactersPerGlyphBuffer);
399 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
401 GlyphMetrics glyphMetrics;
402 GetGlyphsMetrics(glyphIndex,
403 numberOfGLyphsInGroup,
410 // If glyph is WhiteSpace then:
411 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
412 // the endOfLine in RTL was the headOfLine for layouting.
413 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
414 if(RTL == bidirectionalLineInfo.direction)
416 length += glyphMetrics.advance;
420 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
422 penX += glyphMetrics.advance;
426 // If glyph is not whiteSpace then:
427 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
428 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
429 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
430 // Otherwise the current length is maximum.
431 if(LTR == bidirectionalLineInfo.direction)
433 whiteSpaceLengthEndOfLine = 0.f;
435 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
436 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
441 void ReorderBiDiLayout(const Parameters& parameters,
442 LayoutBidiParameters& bidiParameters,
443 const LineLayout& currentLineLayout,
444 LineLayout& lineLayout,
445 bool breakInCharacters,
446 bool enforceEllipsisInSingleLine)
448 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
450 // The last glyph to be laid-out.
451 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
453 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
455 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
456 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
457 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
459 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
461 // Sets the visual to logical map tables needed to reorder the text.
462 ReorderLine(bidirectionalParagraphInfo,
463 bidirectionalLinesInfo,
464 bidiParameters.bidiLineIndex,
465 lineLayout.characterIndex,
466 lineLayout.numberOfCharacters,
467 lineLayout.characterIndexInSecondHalfLine,
468 lineLayout.numberOfCharactersInSecondHalfLine,
469 bidiParameters.paragraphDirection);
471 // Recalculate the length of the line and update the layout.
472 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
474 if(!bidirectionalLineInfo.isIdentity)
477 float whiteSpaceLengthEndOfLine = 0.f;
478 LayoutRightToLeft(parameters,
479 bidirectionalLineInfo,
481 whiteSpaceLengthEndOfLine);
483 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
484 if(!Equals(length, lineLayout.length))
486 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
488 if(isMultiline && (length > parameters.boundingBox.width))
490 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
492 // The word doesn't fit in one line. It has to be split by character.
494 // Remove the last laid out glyph(s) as they doesn't fit.
495 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
497 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
498 lastGlyphOfParagraphPlusOne,
499 charactersPerGlyphBuffer);
501 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
503 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
504 lineLayout.numberOfCharacters -= numberOfCharacters;
506 AdjustLayout(parameters,
508 bidirectionalParagraphInfo,
511 if(lineLayout.length < parameters.boundingBox.width)
516 if(glyphIndex < numberOfGLyphsInGroup)
518 // avoids go under zero for an unsigned int.
522 glyphIndex -= numberOfGLyphsInGroup;
527 lineLayout = currentLineLayout;
529 AdjustLayout(parameters,
531 bidirectionalParagraphInfo,
537 lineLayout.length = std::max(length, lineLayout.length);
544 void AdjustLayout(const Parameters& parameters,
545 LayoutBidiParameters& bidiParameters,
546 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
547 LineLayout& lineLayout)
549 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
551 // Remove current reordered line.
552 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
554 // Re-build the conversion table without the removed glyphs.
555 ReorderLine(bidirectionalParagraphInfo,
556 bidirectionalLinesInfo,
557 bidiParameters.bidiLineIndex,
558 lineLayout.characterIndex,
559 lineLayout.numberOfCharacters,
560 lineLayout.characterIndexInSecondHalfLine,
561 lineLayout.numberOfCharactersInSecondHalfLine,
562 bidiParameters.paragraphDirection);
564 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
567 float whiteSpaceLengthEndOfLine = 0.f;
568 LayoutRightToLeft(parameters,
569 bidirectionalLineInfo,
571 whiteSpaceLengthEndOfLine);
573 lineLayout.length = length;
574 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
578 * Retrieves the line layout for a given box width.
580 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
581 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
582 * to reorder the line and recalculate its length.
585 * @param[in] parameters The layout parameters.
586 * @param[] bidiParameters Bidirectional info for the current line.
587 * @param[out] lineLayout The line layout.
588 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
589 * @param[in] ellipsisPosition Where is the location the text elide
591 void GetLineLayoutForBox(const Parameters& parameters,
592 LayoutBidiParameters& bidiParameters,
593 LineLayout& lineLayout,
595 DevelText::EllipsisPosition::Type ellipsisPosition,
596 bool enforceEllipsisInSingleLine,
597 bool elideTextEnabled)
599 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
600 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
602 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
603 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
604 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
605 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
606 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
608 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
609 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
611 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
612 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
613 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
614 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
615 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
616 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
618 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
620 // The last glyph to be laid-out.
621 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
623 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
624 // In the case the line starts with a right to left character, if the width is longer than the advance,
625 // the difference needs to be added to the line length.
627 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
628 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
629 lastGlyphOfParagraphPlusOne,
630 charactersPerGlyphBuffer);
632 float targetWidth = parameters.boundingBox.width;
633 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
635 bool isSecondHalf = false;
637 GlyphMetrics glyphMetrics;
638 GetGlyphsMetrics(lineLayout.glyphIndex,
639 numberOfGLyphsInGroup,
644 // Set the direction of the first character of the line.
645 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
647 // Stores temporary line layout which has not been added to the final line layout.
648 LineLayout tmpLineLayout;
650 // Initialize the start point.
652 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
653 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
654 // 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.
655 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
657 // Calculate the line height if there is no characters.
658 FontId lastFontId = glyphMetrics.fontId;
659 UpdateLineHeight(glyphMetrics, tmpLineLayout);
661 bool oneWordLaidOut = false;
662 bool oneHyphenLaidOut = false;
663 GlyphIndex hyphenIndex = 0;
664 GlyphInfo hyphenGlyph;
666 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
667 glyphIndex < lastGlyphOfParagraphPlusOne;)
669 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
671 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
672 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
673 lastGlyphOfParagraphPlusOne,
674 charactersPerGlyphBuffer);
676 GlyphMetrics glyphMetrics;
677 GetGlyphsMetrics(glyphIndex,
678 numberOfGLyphsInGroup,
683 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
685 // Check if the font of the current glyph is the same of the previous one.
686 // If it's different the ascender and descender need to be updated.
687 if(lastFontId != glyphMetrics.fontId)
689 UpdateLineHeight(glyphMetrics, tmpLineLayout);
690 lastFontId = glyphMetrics.fontId;
693 // Get the character indices for the current glyph. The last character index is needed
694 // because there are glyphs formed by more than one character but their break info is
695 // given only for the last character.
696 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
697 const bool hasCharacters = charactersPerGlyph > 0u;
698 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
699 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
701 // Get the line break info for the current character.
702 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
706 // Increase the number of characters.
707 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
709 // Increase the number of glyphs.
710 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
714 // Increase the number of characters.
715 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
717 // Increase the number of glyphs.
718 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
721 // Check whether is a white space.
722 const Character character = *(textBuffer + characterFirstIndex);
723 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
725 // Calculate the length of the line.
727 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
728 const float previousTmpPenX = tmpLineLayout.penX;
729 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
730 const float previousTmpLength = tmpLineLayout.length;
731 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
735 // Add the length to the length of white spaces at the end of the line.
736 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
740 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
741 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
743 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
745 // Clear the white space length at the end of the line.
746 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
749 if(isSplitToTwoHalves && (!isSecondHalf) &&
750 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
752 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
753 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
755 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
756 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
758 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
759 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
761 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
763 // Check if the accumulated length fits in the width of the box.
764 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
765 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
766 completelyFill && !isMultiline &&
767 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
769 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
771 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
773 GlyphMetrics glyphMetrics;
774 GetGlyphsMetrics(glyphIndexToRemove,
775 numberOfGLyphsInGroup,
780 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
781 lastGlyphOfParagraphPlusOne,
782 charactersPerGlyphBuffer);
784 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
785 const bool hasCharacters = charactersPerGlyph > 0u;
786 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
787 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
789 // Check whether is a white space.
790 const Character character = *(textBuffer + characterFirstIndex);
791 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
795 // Decrease the number of characters for SecondHalf.
796 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
798 // Decrease the number of glyphs for SecondHalf.
799 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
803 // Decrease the number of characters.
804 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
806 // Decrease the number of glyphs.
807 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
810 if(isRemovedGlyphWhiteSpace)
812 tmpLineLayout.penX -= glyphMetrics.advance;
813 tmpLineLayout.length -= glyphMetrics.advance;
817 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
818 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
823 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
824 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
825 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
829 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
830 tmpLineLayout.characterIndex = characterLastIndex + 1u;
831 glyphIndexToRemove = tmpLineLayout.glyphIndex;
835 else if((completelyFill || isMultiline) &&
836 (tmpLineLayout.length > targetWidth))
838 // Current word does not fit in the box's width.
839 if(((oneHyphenLaidOut && isHyphenMode) ||
840 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
843 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
844 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
847 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
849 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
851 // The word doesn't fit in the control's width. It needs to be split by character.
852 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
856 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
857 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
861 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
862 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
865 tmpLineLayout.penX = previousTmpPenX;
866 tmpLineLayout.previousAdvance = previousTmpAdvance;
867 tmpLineLayout.length = previousTmpLength;
868 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
871 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
873 // Add part of the word to the line layout and shift the first glyph.
874 MergeLineLayout(lineLayout, tmpLineLayout, true);
876 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
877 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
879 // Add part of the word to the line layout.
880 MergeLineLayout(lineLayout, tmpLineLayout, false);
885 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
888 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
890 // Reorder the RTL line.
891 if(bidiParameters.isBidirectional)
893 ReorderBiDiLayout(parameters,
898 enforceEllipsisInSingleLine);
904 if((isMultiline || isLastGlyph) &&
905 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
907 LineLayout currentLineLayout = lineLayout;
908 oneHyphenLaidOut = false;
910 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
912 // Must break the line. Update the line layout, shift the first glyph and return.
913 MergeLineLayout(lineLayout, tmpLineLayout, true);
917 // Must break the line. Update the line layout and return.
918 MergeLineLayout(lineLayout, tmpLineLayout, false);
921 // Reorder the RTL line.
922 if(bidiParameters.isBidirectional)
924 ReorderBiDiLayout(parameters,
929 enforceEllipsisInSingleLine);
932 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
933 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
939 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
941 oneHyphenLaidOut = false;
942 oneWordLaidOut = isWordLaidOut;
943 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
945 // Current glyph is the last one of the current word.
946 // Add the temporal layout to the current one.
947 MergeLineLayout(lineLayout, tmpLineLayout, false);
949 tmpLineLayout.Clear();
953 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
954 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
956 hyphenGlyph = GlyphInfo();
957 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
959 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
960 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
962 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
964 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
966 hyphenIndex = glyphIndex;
967 oneHyphenLaidOut = true;
969 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
971 // Current glyph is the last one of the current word hyphen.
972 // Add the temporal layout to the current one.
973 MergeLineLayout(lineLayout, tmpLineLayout, false);
975 tmpLineLayout.Clear();
979 glyphIndex += numberOfGLyphsInGroup;
982 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
985 void SetGlyphPositions(const Parameters& layoutParameters,
986 Vector2* glyphPositionsBuffer,
987 const LineLayout& layout)
989 // Traverse the glyphs and set the positions.
991 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
992 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
993 const Length numberOfGlyphs = layout.numberOfGlyphs;
994 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
996 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
997 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
999 // Check if the x bearing of the first character is negative.
1000 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1001 // so the penX position needs to be moved to the right.
1002 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1003 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1005 for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
1007 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph + i);
1008 Vector2& position = *(glyphPositionsBuffer + startIndexForGlyphPositions + i);
1010 position.x = penX + glyph.xBearing;
1011 position.y = -glyph.yBearing;
1013 penX += (glyph.advance + interGlyphExtraAdvance);
1016 if(layout.isSplitToTwoHalves)
1018 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1019 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1020 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1022 for(GlyphIndex i = 0u; i < numberOfGlyphsInSecondHalfLine; ++i)
1024 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyphInSecondHalf + i);
1025 Vector2& position = *(glyphPositionsBuffer + startIndexForGlyphPositionsnSecondHalf + i);
1027 position.x = penX + glyph.xBearing;
1028 position.y = -glyph.yBearing;
1030 penX += (glyph.advance + interGlyphExtraAdvance);
1035 void SetGlyphPositions(const Parameters& layoutParameters,
1036 Vector2* glyphPositionsBuffer,
1037 LayoutBidiParameters& layoutBidiParameters,
1038 const LineLayout& layout)
1040 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
1041 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1042 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1043 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1044 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
1046 CharacterIndex characterLogicalIndex = 0u;
1047 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1048 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1052 if(layout.isSplitToTwoHalves)
1054 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
1056 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1057 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1059 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1061 position.y = -glyph.yBearing;
1063 penX += glyph.advance;
1065 ++characterLogicalIndex;
1066 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1070 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1072 extendedToSecondHalf = true;
1073 characterLogicalIndex = 0u;
1074 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1076 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
1078 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1079 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1081 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1083 position.y = -glyph.yBearing;
1085 penX += glyph.advance;
1087 ++characterLogicalIndex;
1088 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1092 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1093 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1095 penX += -glyph.xBearing;
1097 // Traverses the characters of the right to left paragraph.
1098 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1100 for(; characterLogicalIndex < bidiLine.characterRunForSecondHalfLine.numberOfCharacters;
1101 ++characterLogicalIndex)
1103 // Convert the character in the logical order into the character in the visual order.
1104 const CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1106 // Get the number of glyphs of the character.
1107 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
1109 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
1111 // Convert the character in the visual order into the glyph in the visual order.
1112 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
1114 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
1116 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1117 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1119 position.x = penX + glyph.xBearing;
1120 position.y = -glyph.yBearing;
1122 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
1127 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1128 for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1129 ++characterLogicalIndex)
1131 // Convert the character in the logical order into the character in the visual order.
1132 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1134 // Get the number of glyphs of the character.
1135 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
1137 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
1139 // Convert the character in the visual order into the glyph in the visual order.
1140 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
1142 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
1144 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1145 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1147 position.x = penX + glyph.xBearing;
1148 position.y = -glyph.yBearing;
1150 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
1156 * @brief Resizes the line buffer.
1158 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1159 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1160 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1161 * @param[in] updateCurrentBuffer Whether the layout is updated.
1163 * @return Pointer to either lines or newLines.
1165 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1166 Vector<LineRun>& newLines,
1167 Length& linesCapacity,
1168 bool updateCurrentBuffer)
1170 LineRun* linesBuffer = nullptr;
1171 // Reserve more space for the next lines.
1172 linesCapacity *= 2u;
1173 if(updateCurrentBuffer)
1175 newLines.Resize(linesCapacity);
1176 linesBuffer = newLines.Begin();
1180 lines.Resize(linesCapacity);
1181 linesBuffer = lines.Begin();
1188 * Ellipsis a line if it exceeds the width's of the bounding box.
1190 * @param[in] layoutParameters The parameters needed to layout the text.
1191 * @param[in] layout The line layout.
1192 * @param[in,out] layoutSize The text's layout size.
1193 * @param[in,out] linesBuffer Pointer to the line's buffer.
1194 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1195 * @param[in,out] numberOfLines The number of laid-out lines.
1196 * @param[in] penY The vertical layout position.
1197 * @param[in] currentParagraphDirection The current paragraph's direction.
1198 * @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
1199 * @param[in] ellipsisPosition Where is the location the text elide
1201 * return Whether the line is ellipsized.
1203 bool EllipsisLine(const Parameters& layoutParameters,
1204 LayoutBidiParameters& layoutBidiParameters,
1205 const LineLayout& layout,
1207 LineRun* linesBuffer,
1208 Vector2* glyphPositionsBuffer,
1209 Length& numberOfLines,
1211 bool& isAutoScrollEnabled,
1212 DevelText::EllipsisPosition::Type ellipsisPosition,
1213 bool enforceEllipsisInSingleLine)
1215 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))));
1216 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1217 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1219 isAutoScrollEnabled = false;
1220 // Do not layout more lines if ellipsis is enabled.
1222 // The last line needs to be completely filled with characters.
1223 // Part of a word may be used.
1225 LineRun* lineRun = nullptr;
1226 LineLayout ellipsisLayout;
1227 if(0u != numberOfLines)
1229 // Get the last line and layout it again with the 'completelyFill' flag to true.
1230 lineRun = linesBuffer + (numberOfLines - 1u);
1231 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1233 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1237 // At least there is space reserved for one line.
1238 lineRun = linesBuffer;
1240 lineRun->glyphRun.glyphIndex = 0u;
1241 ellipsisLayout.glyphIndex = 0u;
1242 lineRun->isSplitToTwoHalves = false;
1247 GetLineLayoutForBox(layoutParameters,
1248 layoutBidiParameters,
1252 enforceEllipsisInSingleLine,
1255 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1257 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1260 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1261 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1262 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1263 lineRun->width = ellipsisLayout.length;
1264 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1265 lineRun->ascender = ellipsisLayout.ascender;
1266 lineRun->descender = ellipsisLayout.descender;
1267 lineRun->ellipsis = true;
1269 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1270 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1271 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1272 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1273 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1275 layoutSize.width = layoutParameters.boundingBox.width;
1276 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1278 layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
1281 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1283 if(layoutBidiParameters.isBidirectional)
1285 layoutBidiParameters.bidiLineIndex = 0u;
1286 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1287 endIt = bidirectionalLinesInfo.End();
1289 ++it, ++layoutBidiParameters.bidiLineIndex)
1291 const BidirectionalLineInfoRun& run = *it;
1292 //To handle case when the laid characters exist in next line.
1293 //More than one BidirectionalLineInfoRun could start with same character.
1294 //When need to check also numberOfCharacters in line.
1295 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1296 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1297 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1298 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1299 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1301 // Found where to insert the bidi line info.
1307 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1309 if((nullptr != bidirectionalLineInfo) &&
1310 !bidirectionalLineInfo->isIdentity &&
1311 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1313 lineRun->direction = RTL;
1314 SetGlyphPositions(layoutParameters,
1315 glyphPositionsBuffer,
1316 layoutBidiParameters,
1321 lineRun->direction = LTR;
1322 SetGlyphPositions(layoutParameters,
1323 glyphPositionsBuffer,
1332 * @brief Updates the text layout with a new laid-out line.
1334 * @param[in] layoutParameters The parameters needed to layout the text.
1335 * @param[in] layout The line layout.
1336 * @param[in,out] layoutSize The text's layout size.
1337 * @param[in,out] linesBuffer Pointer to the line's buffer.
1338 * @param[in] index Index to the vector of glyphs.
1339 * @param[in,out] numberOfLines The number of laid-out lines.
1340 * @param[in] isLastLine Whether the laid-out line is the last one.
1342 void UpdateTextLayout(const Parameters& layoutParameters,
1343 const LineLayout& layout,
1345 LineRun* linesBuffer,
1347 Length& numberOfLines,
1350 LineRun& lineRun = *(linesBuffer + numberOfLines);
1353 lineRun.glyphRun.glyphIndex = index;
1354 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1355 lineRun.characterRun.characterIndex = layout.characterIndex;
1356 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1357 lineRun.width = layout.length;
1358 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1360 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1361 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1362 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1363 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1364 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1366 // Rounds upward to avoid a non integer size.
1367 lineRun.width = std::ceil(lineRun.width);
1369 lineRun.ascender = layout.ascender;
1370 lineRun.descender = layout.descender;
1371 lineRun.direction = layout.direction;
1372 lineRun.ellipsis = false;
1374 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1375 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1377 lineRun.lineSpacing += mDefaultLineSpacing;
1379 // Update the actual size.
1380 if(lineRun.width > layoutSize.width)
1382 layoutSize.width = lineRun.width;
1385 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
1389 * @brief Updates the text layout with the last laid-out line.
1391 * @param[in] layoutParameters The parameters needed to layout the text.
1392 * @param[in] characterIndex The character index of the line.
1393 * @param[in] glyphIndex The glyph index of the line.
1394 * @param[in,out] layoutSize The text's layout size.
1395 * @param[in,out] linesBuffer Pointer to the line's buffer.
1396 * @param[in,out] numberOfLines The number of laid-out lines.
1398 void UpdateTextLayout(const Parameters& layoutParameters,
1399 CharacterIndex characterIndex,
1400 GlyphIndex glyphIndex,
1402 LineRun* linesBuffer,
1403 Length& numberOfLines)
1405 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1407 // Need to add a new line with no characters but with height to increase the layoutSize.height
1408 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1410 Text::FontMetrics fontMetrics;
1411 if(0u != glyphInfo.fontId)
1413 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1416 LineRun& lineRun = *(linesBuffer + numberOfLines);
1419 lineRun.glyphRun.glyphIndex = glyphIndex;
1420 lineRun.glyphRun.numberOfGlyphs = 0u;
1421 lineRun.characterRun.characterIndex = characterIndex;
1422 lineRun.characterRun.numberOfCharacters = 0u;
1423 lineRun.width = 0.f;
1424 lineRun.ascender = fontMetrics.ascender;
1425 lineRun.descender = fontMetrics.descender;
1426 lineRun.extraLength = 0.f;
1427 lineRun.alignmentOffset = 0.f;
1428 lineRun.direction = LTR;
1429 lineRun.ellipsis = false;
1431 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1432 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1434 lineRun.lineSpacing += mDefaultLineSpacing;
1436 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
1440 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1442 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1443 * @param[in,out] layoutSize The text's layout size.
1445 void UpdateLayoutSize(const Vector<LineRun>& lines,
1448 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1449 endIt = lines.End();
1453 const LineRun& line = *it;
1455 if(line.width > layoutSize.width)
1457 layoutSize.width = line.width;
1460 layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
1465 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1467 * @param[in] layoutParameters The parameters needed to layout the text.
1468 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1469 * @param[in] characterOffset The offset to be added to the runs of characters.
1470 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1472 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1473 Vector<LineRun>& lines,
1474 Length characterOffset,
1477 // Update the glyph and character runs.
1478 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1479 endIt = lines.End();
1483 LineRun& line = *it;
1485 line.glyphRun.glyphIndex = glyphOffset;
1486 line.characterRun.characterIndex = characterOffset;
1488 glyphOffset += line.glyphRun.numberOfGlyphs;
1489 characterOffset += line.characterRun.numberOfCharacters;
1493 bool LayoutText(Parameters& layoutParameters,
1495 bool elideTextEnabled,
1496 bool& isAutoScrollEnabled,
1497 DevelText::EllipsisPosition::Type ellipsisPosition)
1499 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1500 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1502 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1503 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1505 //Reset indices of ElidedGlyphs
1506 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1507 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1508 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1509 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1511 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1513 if(0u == layoutParameters.numberOfGlyphs)
1515 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1516 if(layoutParameters.isLastNewParagraph)
1518 Length numberOfLines = lines.Count();
1519 if(0u != numberOfLines)
1521 const LineRun& lastLine = *(lines.End() - 1u);
1523 if(0u != lastLine.characterRun.numberOfCharacters)
1525 // Need to add a new line with no characters but with height to increase the layoutSize.height
1527 Initialize(newLine);
1528 lines.PushBack(newLine);
1530 UpdateTextLayout(layoutParameters,
1531 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1532 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1540 // Calculates the layout size.
1541 UpdateLayoutSize(lines,
1544 // Rounds upward to avoid a non integer size.
1545 layoutSize.height = std::ceil(layoutSize.height);
1547 // Nothing else do if there are no glyphs to layout.
1551 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1552 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1553 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1555 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1556 // This extra line needs to be removed.
1557 if(0u != lines.Count())
1559 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1561 if((0u == lastLine->characterRun.numberOfCharacters) &&
1562 (lastGlyphPlusOne == totalNumberOfGlyphs))
1564 lines.Remove(lastLine);
1568 // Retrieve BiDi info.
1569 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1571 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1572 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1573 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1575 // Set the layout bidirectional paramters.
1576 LayoutBidiParameters layoutBidiParameters;
1578 // Whether the layout is being updated or set from scratch.
1579 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1581 Vector2* glyphPositionsBuffer = nullptr;
1582 Vector<Vector2> newGlyphPositions;
1584 LineRun* linesBuffer = nullptr;
1585 Vector<LineRun> newLines;
1587 // Estimate the number of lines.
1588 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1589 Length numberOfLines = 0u;
1591 if(updateCurrentBuffer)
1593 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1594 glyphPositionsBuffer = newGlyphPositions.Begin();
1596 newLines.Resize(linesCapacity);
1597 linesBuffer = newLines.Begin();
1601 glyphPositionsBuffer = glyphPositions.Begin();
1603 lines.Resize(linesCapacity);
1604 linesBuffer = lines.Begin();
1607 float penY = CalculateLineOffset(lines,
1608 layoutParameters.startLineIndex);
1609 bool anyLineIsEliped = false;
1610 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1612 layoutBidiParameters.Clear();
1614 if(hasBidiParagraphs)
1616 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1618 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1619 endIt = bidirectionalParagraphsInfo.End();
1621 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1623 const BidirectionalParagraphInfoRun& run = *it;
1625 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1627 if(lastCharacterIndex <= startCharacterIndex)
1629 // Do not process, the paragraph has already been processed.
1633 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1635 layoutBidiParameters.paragraphDirection = run.direction;
1636 layoutBidiParameters.isBidirectional = true;
1639 // Has already been found.
1643 if(layoutBidiParameters.isBidirectional)
1645 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1646 endIt = bidirectionalLinesInfo.End();
1648 ++it, ++layoutBidiParameters.bidiLineIndex)
1650 const BidirectionalLineInfoRun& run = *it;
1652 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1654 if(lastCharacterIndex <= startCharacterIndex)
1660 if(startCharacterIndex < lastCharacterIndex)
1662 // Found where to insert the bidi line info.
1669 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1671 // Get the layout for the line.
1673 layout.direction = layoutBidiParameters.paragraphDirection;
1674 layout.glyphIndex = index;
1675 GetLineLayoutForBox(layoutParameters,
1676 layoutBidiParameters,
1683 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1684 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1685 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1686 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1687 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1689 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1691 // The width is too small and no characters are laid-out.
1692 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1694 lines.Resize(numberOfLines);
1696 // Rounds upward to avoid a non integer size.
1697 layoutSize.height = std::ceil(layoutSize.height);
1702 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1704 penY += layout.ascender;
1706 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1708 bool ellipsis = false;
1709 if(elideTextEnabled)
1711 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1713 // Does the ellipsis of the last line.
1714 ellipsis = EllipsisLine(layoutParameters,
1715 layoutBidiParameters,
1719 glyphPositionsBuffer,
1722 isAutoScrollEnabled,
1727 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1729 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1730 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1732 ellipsis = EllipsisLine(layoutParameters,
1733 layoutBidiParameters,
1737 glyphPositionsBuffer,
1740 isAutoScrollEnabled,
1745 //clear hyphen from ellipsis line
1746 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1747 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1749 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1751 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1752 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1756 // No more lines to layout.
1761 //In START location of ellipsis whether to shift lines or not.
1762 anyLineIsEliped |= ellipsis;
1764 // Whether the last line has been laid-out.
1765 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1767 if(numberOfLines == linesCapacity)
1769 // Reserve more space for the next lines.
1770 linesBuffer = ResizeLinesBuffer(lines,
1773 updateCurrentBuffer);
1776 // Updates the current text's layout with the line's layout.
1777 UpdateTextLayout(layoutParameters,
1785 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1787 if((nextIndex == totalNumberOfGlyphs) &&
1788 layoutParameters.isLastNewParagraph &&
1789 (mLayout == MULTI_LINE_BOX))
1791 // The last character of the text is a new paragraph character.
1792 // An extra line with no characters is added to increase the text's height
1793 // in order to place the cursor.
1795 if(numberOfLines == linesCapacity)
1797 // Reserve more space for the next lines.
1798 linesBuffer = ResizeLinesBuffer(lines,
1801 updateCurrentBuffer);
1804 UpdateTextLayout(layoutParameters,
1805 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1806 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1810 } // whether to add a last line.
1812 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1814 if((nullptr != bidirectionalLineInfo) &&
1815 !bidirectionalLineInfo->isIdentity &&
1816 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1818 SetGlyphPositions(layoutParameters,
1819 glyphPositionsBuffer,
1820 layoutBidiParameters,
1825 // Sets the positions of the glyphs.
1826 SetGlyphPositions(layoutParameters,
1827 glyphPositionsBuffer,
1831 // Updates the vertical pen's position.
1832 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1833 // If there is a defaultLineSize, updates the pen's position.
1834 if(mDefaultLineSize > 0.f)
1836 float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1837 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1838 penY += lineSpacing;
1841 // Increase the glyph index.
1844 } // end for() traversing glyphs.
1846 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1847 if(anyLineIsEliped && numberOfLines > 1u)
1849 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1851 Length lineIndex = 0;
1852 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1854 LineRun& delLine = linesBuffer[lineIndex];
1855 delLine.ellipsis = true;
1857 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1858 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1860 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1861 linesBuffer[lineIndex].ellipsis = false;
1866 linesBuffer[0u].ellipsis = true;
1868 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1870 Length middleLineIndex = (numberOfLines) / 2u;
1871 Length ellipsisLineIndex = 0u;
1872 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1874 LineRun& delLine = linesBuffer[middleLineIndex];
1875 delLine.ellipsis = true;
1877 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1878 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1880 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1881 linesBuffer[lineIndex].ellipsis = false;
1884 ellipsisLineIndex = middleLineIndex - 1u;
1885 middleLineIndex = (numberOfLines) / 2u;
1888 linesBuffer[ellipsisLineIndex].ellipsis = true;
1892 if(updateCurrentBuffer)
1894 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1895 newGlyphPositions.Begin(),
1896 newGlyphPositions.End());
1897 glyphPositions.Resize(totalNumberOfGlyphs);
1899 newLines.Resize(numberOfLines);
1901 // Current text's layout size adds only the newly laid-out lines.
1902 // Updates the layout size with the previously laid-out lines.
1903 UpdateLayoutSize(lines,
1906 if(0u != newLines.Count())
1908 const LineRun& lastLine = *(newLines.End() - 1u);
1910 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1911 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1913 // Update the indices of the runs before the new laid-out lines are inserted.
1914 UpdateLineIndexOffsets(layoutParameters,
1919 // Insert the lines.
1920 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1927 lines.Resize(numberOfLines);
1930 // Rounds upward to avoid a non integer size.
1931 layoutSize.height = std::ceil(layoutSize.height);
1933 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1938 void Align(const Size& size,
1939 CharacterIndex startIndex,
1940 Length numberOfCharacters,
1941 Text::HorizontalAlignment::Type horizontalAlignment,
1942 Vector<LineRun>& lines,
1943 float& alignmentOffset,
1944 Dali::LayoutDirection::Type layoutDirection,
1945 bool matchLayoutDirection)
1947 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1949 alignmentOffset = MAX_FLOAT;
1950 // Traverse all lines and align the glyphs.
1951 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1955 LineRun& line = *it;
1957 if(line.characterRun.characterIndex < startIndex)
1959 // Do not align lines which have already been aligned.
1963 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1965 // Do not align lines beyond the last laid-out character.
1969 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1971 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1975 // Calculate the line's alignment offset accordingly with the align option,
1976 // the box width, line length, and the paragraph's direction.
1977 CalculateHorizontalAlignment(size.width,
1978 horizontalAlignment,
1981 matchLayoutDirection);
1983 // Updates the alignment offset.
1984 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1988 void CalculateHorizontalAlignment(float boxWidth,
1989 HorizontalAlignment::Type horizontalAlignment,
1991 Dali::LayoutDirection::Type layoutDirection,
1992 bool matchLayoutDirection)
1994 line.alignmentOffset = 0.f;
1995 const bool isLineRTL = RTL == line.direction;
1997 // Whether to swap the alignment.
1998 // 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.
1999 bool isLayoutRTL = isLineRTL;
2000 float lineLength = line.width;
2002 // match align for system language direction
2003 if(matchLayoutDirection)
2005 // Swap the alignment type if the line is right to left.
2006 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2008 // Calculate the horizontal line offset.
2009 switch(horizontalAlignment)
2011 case HorizontalAlignment::BEGIN:
2017 lineLength += line.extraLength;
2020 line.alignmentOffset = boxWidth - lineLength;
2024 line.alignmentOffset = 0.f;
2028 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2029 line.alignmentOffset -= line.extraLength;
2034 case HorizontalAlignment::CENTER:
2036 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2040 line.alignmentOffset -= line.extraLength;
2043 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2046 case HorizontalAlignment::END:
2050 line.alignmentOffset = 0.f;
2054 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2055 line.alignmentOffset -= line.extraLength;
2062 lineLength += line.extraLength;
2065 line.alignmentOffset = boxWidth - lineLength;
2072 void Initialize(LineRun& line)
2074 line.glyphRun.glyphIndex = 0u;
2075 line.glyphRun.numberOfGlyphs = 0u;
2076 line.characterRun.characterIndex = 0u;
2077 line.characterRun.numberOfCharacters = 0u;
2079 line.ascender = 0.f;
2080 line.descender = 0.f;
2081 line.extraLength = 0.f;
2082 line.alignmentOffset = 0.f;
2083 line.direction = LTR;
2084 line.ellipsis = false;
2085 line.lineSpacing = mDefaultLineSpacing;
2086 line.isSplitToTwoHalves = false;
2087 line.glyphRunSecondHalf.glyphIndex = 0u;
2088 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2089 line.characterRunForSecondHalfLine.characterIndex = 0u;
2090 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2095 float mDefaultLineSpacing;
2096 float mDefaultLineSize;
2098 IntrusivePtr<Metrics> mMetrics;
2104 mImpl = new Engine::Impl();
2112 void Engine::SetMetrics(MetricsPtr& metrics)
2114 mImpl->mMetrics = metrics;
2117 void Engine::SetLayout(Type layout)
2119 mImpl->mLayout = layout;
2122 Engine::Type Engine::GetLayout() const
2124 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2125 return mImpl->mLayout;
2128 void Engine::SetCursorWidth(int width)
2130 mImpl->mCursorWidth = static_cast<float>(width);
2133 int Engine::GetCursorWidth() const
2135 return static_cast<int>(mImpl->mCursorWidth);
2138 bool Engine::LayoutText(Parameters& layoutParameters,
2140 bool elideTextEnabled,
2141 bool& isAutoScrollEnabled,
2142 DevelText::EllipsisPosition::Type ellipsisPosition)
2144 return mImpl->LayoutText(layoutParameters,
2147 isAutoScrollEnabled,
2151 void Engine::Align(const Size& size,
2152 CharacterIndex startIndex,
2153 Length numberOfCharacters,
2154 Text::HorizontalAlignment::Type horizontalAlignment,
2155 Vector<LineRun>& lines,
2156 float& alignmentOffset,
2157 Dali::LayoutDirection::Type layoutDirection,
2158 bool matchLayoutDirection)
2163 horizontalAlignment,
2167 matchLayoutDirection);
2170 void Engine::SetDefaultLineSpacing(float lineSpacing)
2172 mImpl->mDefaultLineSpacing = lineSpacing;
2175 float Engine::GetDefaultLineSpacing() const
2177 return mImpl->mDefaultLineSpacing;
2180 void Engine::SetDefaultLineSize(float lineSize)
2182 mImpl->mDefaultLineSize = lineSize;
2185 float Engine::GetDefaultLineSize() const
2187 return mImpl->mDefaultLineSize;
2190 } // namespace Layout
2194 } // namespace Toolkit