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>
39 float GetLineHeight(const LineRun lineRun)
41 // The line height is the addition of the line ascender, the line descender and the line spacing.
42 // However, the line descender has a negative value, hence the subtraction.
43 return lineRun.ascender - lineRun.descender + lineRun.lineSpacing;
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
53 const float MAX_FLOAT = std::numeric_limits<float>::max();
54 const CharacterDirection LTR = false;
55 const CharacterDirection RTL = !LTR;
56 const float LINE_SPACING = 0.f;
57 const float MIN_LINE_SIZE = 0.f;
58 const Character HYPHEN_UNICODE = 0x002D;
60 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
62 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
68 * @brief Stores temporary layout info of the line.
76 numberOfCharacters{0u},
83 whiteSpaceLengthEndOfLine{0.f},
85 isSplitToTwoHalves(false),
86 glyphIndexInSecondHalfLine{0u},
87 characterIndexInSecondHalfLine{0u},
88 numberOfGlyphsInSecondHalfLine{0u},
89 numberOfCharactersInSecondHalfLine{0u}
103 numberOfCharacters = 0u;
104 ascender = -MAX_FLOAT;
105 descender = MAX_FLOAT;
107 isSplitToTwoHalves = false;
108 glyphIndexInSecondHalfLine = 0u;
109 characterIndexInSecondHalfLine = 0u;
110 numberOfGlyphsInSecondHalfLine = 0u;
111 numberOfCharactersInSecondHalfLine = 0u;
114 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
115 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
116 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
117 Length numberOfCharacters; ///< The number of characters which fit in one line.
118 float ascender; ///< The maximum ascender of all fonts in the line.
119 float descender; ///< The minimum descender of all fonts in the line.
120 float lineSpacing; ///< The line spacing
121 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
122 float previousAdvance; ///< The advance of the previous glyph.
123 float length; ///< The current length of the line.
124 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
125 CharacterDirection direction;
127 bool isSplitToTwoHalves; ///< Whether the second half is defined.
128 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
129 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
130 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
131 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
134 struct LayoutBidiParameters
138 paragraphDirection = LTR;
139 bidiParagraphIndex = 0u;
141 isBidirectional = false;
144 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
145 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
146 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
147 bool isBidirectional = false; ///< Whether the text is bidirectional.
153 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
155 mDefaultLineSpacing{LINE_SPACING},
156 mDefaultLineSize{MIN_LINE_SIZE}
161 * @brief Updates the line ascender and descender with the metrics of a new font.
163 * @param[in] glyphMetrics The metrics of the new font.
164 * @param[in,out] lineLayout The line layout.
166 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
168 Text::FontMetrics fontMetrics;
169 if(0u != glyphMetrics.fontId)
171 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
175 fontMetrics.ascender = glyphMetrics.fontHeight;
176 fontMetrics.descender = 0.f;
177 fontMetrics.height = fontMetrics.ascender;
178 fontMetrics.underlinePosition = 0.f;
179 fontMetrics.underlineThickness = 1.f;
182 // Sets the maximum ascender.
183 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
185 // Sets the minimum descender.
186 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
188 // Sets the line size
189 lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
190 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
192 // Add the line spacing
193 lineLayout.lineSpacing += mDefaultLineSpacing;
197 * @brief Merges a temporary line layout into the line layout.
199 * @param[in,out] lineLayout The line layout.
200 * @param[in] tmpLineLayout A temporary line layout.
201 * @param[in] isShifted Whether to shift first glyph and character indices.
203 void MergeLineLayout(LineLayout& lineLayout,
204 const LineLayout& tmpLineLayout,
207 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
208 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
210 lineLayout.penX = tmpLineLayout.penX;
211 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
213 lineLayout.length = tmpLineLayout.length;
214 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
216 // Sets the maximum ascender.
217 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
219 // Sets the minimum descender.
220 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
222 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
225 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
226 lineLayout.characterIndex = tmpLineLayout.characterIndex;
229 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
230 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
231 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
232 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
233 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
236 void LayoutRightToLeft(const Parameters& parameters,
237 const BidirectionalLineInfoRun& bidirectionalLineInfo,
239 float& whiteSpaceLengthEndOfLine)
241 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
242 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
243 // then the first half of line "characterRun",
244 // Otherwise travers whole characters in"characterRun".
246 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
247 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
248 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
249 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
251 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
252 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
254 CharacterIndex characterLogicalIndex = 0u;
255 CharacterIndex characterVisualIndex = 0u;
257 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
258 // Otherwise maps the first visual index from visualToLogicalMap.
259 // This is to initialize the first visual index.
260 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
262 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
266 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
269 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
271 if(RTL == bidirectionalLineInfo.direction)
273 // If there are characters in the second half of Line.
274 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
276 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
277 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
279 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
281 whiteSpaceLengthEndOfLine += glyphInfo.advance;
283 ++characterLogicalIndex;
284 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
288 // If all characters in the second half of Line are WhiteSpaces.
289 // then continue adding the WhiteSpaces from the first hel of Line.
290 // Also this is valid when the line was not splitted.
291 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
293 extendedToSecondHalf = true; // Whether the logical index is extended to second half
294 characterLogicalIndex = 0u;
295 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
297 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
298 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
300 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
302 whiteSpaceLengthEndOfLine += glyphInfo.advance;
304 ++characterLogicalIndex;
305 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
310 // Here's the first index of character is not WhiteSpace
311 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
313 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
314 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
315 lastGlyphOfParagraphPlusOne,
316 charactersPerGlyphBuffer);
318 GlyphMetrics glyphMetrics;
319 GetGlyphsMetrics(glyphIndex,
320 numberOfGLyphsInGroup,
325 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
327 // Traverses the characters of the right to left paragraph.
328 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
329 if(!extendedToSecondHalf &&
330 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
332 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
334 // Convert the character in the logical order into the character in the visual order.
335 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
336 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
338 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
340 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
341 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
342 lastGlyphOfParagraphPlusOne,
343 charactersPerGlyphBuffer);
345 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
347 GlyphMetrics glyphMetrics;
348 GetGlyphsMetrics(glyphIndex,
349 numberOfGLyphsInGroup,
356 // If glyph is WhiteSpace then:
357 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
358 // the endOfLine in RTL was the headOfLine for layouting.
359 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
360 if(RTL == bidirectionalLineInfo.direction)
362 length += glyphMetrics.advance;
366 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
368 penX += glyphMetrics.advance;
372 // If glyph is not whiteSpace then:
373 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
374 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
375 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
376 // Otherwise the current length is maximum.
377 if(LTR == bidirectionalLineInfo.direction)
379 whiteSpaceLengthEndOfLine = 0.f;
381 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
382 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
387 // Continue traversing in the first half of line or in the whole line.
388 // If the second half of line was extended then continue from logical index in the first half of line
389 // Also this is valid when the line was not splitted and there were WhiteSpace.
390 // Otherwise start from first logical index in line.
391 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
392 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
394 // Convert the character in the logical order into the character in the visual order.
395 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
396 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
398 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
400 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
401 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
402 lastGlyphOfParagraphPlusOne,
403 charactersPerGlyphBuffer);
405 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
407 GlyphMetrics glyphMetrics;
408 GetGlyphsMetrics(glyphIndex,
409 numberOfGLyphsInGroup,
416 // If glyph is WhiteSpace then:
417 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
418 // the endOfLine in RTL was the headOfLine for layouting.
419 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
420 if(RTL == bidirectionalLineInfo.direction)
422 length += glyphMetrics.advance;
426 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
428 penX += glyphMetrics.advance;
432 // If glyph is not whiteSpace then:
433 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
434 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
435 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
436 // Otherwise the current length is maximum.
437 if(LTR == bidirectionalLineInfo.direction)
439 whiteSpaceLengthEndOfLine = 0.f;
441 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
442 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
447 void ReorderBiDiLayout(const Parameters& parameters,
448 LayoutBidiParameters& bidiParameters,
449 const LineLayout& currentLineLayout,
450 LineLayout& lineLayout,
451 bool breakInCharacters,
452 bool enforceEllipsisInSingleLine)
454 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
456 // The last glyph to be laid-out.
457 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
459 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
461 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
462 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
463 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
465 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
467 // Sets the visual to logical map tables needed to reorder the text.
468 ReorderLine(bidirectionalParagraphInfo,
469 bidirectionalLinesInfo,
470 bidiParameters.bidiLineIndex,
471 lineLayout.characterIndex,
472 lineLayout.numberOfCharacters,
473 lineLayout.characterIndexInSecondHalfLine,
474 lineLayout.numberOfCharactersInSecondHalfLine,
475 bidiParameters.paragraphDirection);
477 // Recalculate the length of the line and update the layout.
478 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
480 if(!bidirectionalLineInfo.isIdentity)
483 float whiteSpaceLengthEndOfLine = 0.f;
484 LayoutRightToLeft(parameters,
485 bidirectionalLineInfo,
487 whiteSpaceLengthEndOfLine);
489 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
490 if(!Equals(length, lineLayout.length))
492 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
494 if(isMultiline && (length > parameters.boundingBox.width))
496 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
498 // The word doesn't fit in one line. It has to be split by character.
500 // Remove the last laid out glyph(s) as they doesn't fit.
501 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
503 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
504 lastGlyphOfParagraphPlusOne,
505 charactersPerGlyphBuffer);
507 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
509 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
510 lineLayout.numberOfCharacters -= numberOfCharacters;
512 AdjustLayout(parameters,
514 bidirectionalParagraphInfo,
517 if(lineLayout.length < parameters.boundingBox.width)
522 if(glyphIndex < numberOfGLyphsInGroup)
524 // avoids go under zero for an unsigned int.
528 glyphIndex -= numberOfGLyphsInGroup;
533 lineLayout = currentLineLayout;
535 AdjustLayout(parameters,
537 bidirectionalParagraphInfo,
543 lineLayout.length = std::max(length, lineLayout.length);
550 void AdjustLayout(const Parameters& parameters,
551 LayoutBidiParameters& bidiParameters,
552 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
553 LineLayout& lineLayout)
555 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
557 // Remove current reordered line.
558 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
560 // Re-build the conversion table without the removed glyphs.
561 ReorderLine(bidirectionalParagraphInfo,
562 bidirectionalLinesInfo,
563 bidiParameters.bidiLineIndex,
564 lineLayout.characterIndex,
565 lineLayout.numberOfCharacters,
566 lineLayout.characterIndexInSecondHalfLine,
567 lineLayout.numberOfCharactersInSecondHalfLine,
568 bidiParameters.paragraphDirection);
570 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
573 float whiteSpaceLengthEndOfLine = 0.f;
574 LayoutRightToLeft(parameters,
575 bidirectionalLineInfo,
577 whiteSpaceLengthEndOfLine);
579 lineLayout.length = length;
580 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
584 * Retrieves the line layout for a given box width.
586 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
587 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
588 * to reorder the line and recalculate its length.
591 * @param[in] parameters The layout parameters.
592 * @param[] bidiParameters Bidirectional info for the current line.
593 * @param[out] lineLayout The line layout.
594 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
595 * @param[in] ellipsisPosition Where is the location the text elide
597 void GetLineLayoutForBox(const Parameters& parameters,
598 LayoutBidiParameters& bidiParameters,
599 LineLayout& lineLayout,
601 DevelText::EllipsisPosition::Type ellipsisPosition,
602 bool enforceEllipsisInSingleLine,
603 bool elideTextEnabled)
605 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
606 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
608 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
609 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
610 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
611 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
612 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
614 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
615 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
617 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
618 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
619 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
620 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
621 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
622 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
624 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
626 // The last glyph to be laid-out.
627 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
629 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
630 // In the case the line starts with a right to left character, if the width is longer than the advance,
631 // the difference needs to be added to the line length.
633 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
634 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
635 lastGlyphOfParagraphPlusOne,
636 charactersPerGlyphBuffer);
638 float targetWidth = parameters.boundingBox.width;
639 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
641 bool isSecondHalf = false;
643 GlyphMetrics glyphMetrics;
644 GetGlyphsMetrics(lineLayout.glyphIndex,
645 numberOfGLyphsInGroup,
650 // Set the direction of the first character of the line.
651 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
653 // Stores temporary line layout which has not been added to the final line layout.
654 LineLayout tmpLineLayout;
656 // Initialize the start point.
658 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
659 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
660 // 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.
661 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
663 // Calculate the line height if there is no characters.
664 FontId lastFontId = glyphMetrics.fontId;
665 UpdateLineHeight(glyphMetrics, tmpLineLayout);
667 bool oneWordLaidOut = false;
668 bool oneHyphenLaidOut = false;
669 GlyphIndex hyphenIndex = 0;
670 GlyphInfo hyphenGlyph;
672 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
673 glyphIndex < lastGlyphOfParagraphPlusOne;)
675 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
677 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
678 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
679 lastGlyphOfParagraphPlusOne,
680 charactersPerGlyphBuffer);
682 GlyphMetrics glyphMetrics;
683 GetGlyphsMetrics(glyphIndex,
684 numberOfGLyphsInGroup,
689 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
691 // Check if the font of the current glyph is the same of the previous one.
692 // If it's different the ascender and descender need to be updated.
693 if(lastFontId != glyphMetrics.fontId)
695 UpdateLineHeight(glyphMetrics, tmpLineLayout);
696 lastFontId = glyphMetrics.fontId;
699 // Get the character indices for the current glyph. The last character index is needed
700 // because there are glyphs formed by more than one character but their break info is
701 // given only for the last character.
702 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
703 const bool hasCharacters = charactersPerGlyph > 0u;
704 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
705 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
707 // Get the line break info for the current character.
708 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
712 // Increase the number of characters.
713 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
715 // Increase the number of glyphs.
716 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
720 // Increase the number of characters.
721 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
723 // Increase the number of glyphs.
724 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
727 // Check whether is a white space.
728 const Character character = *(textBuffer + characterFirstIndex);
729 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
731 // Calculate the length of the line.
733 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
734 const float previousTmpPenX = tmpLineLayout.penX;
735 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
736 const float previousTmpLength = tmpLineLayout.length;
737 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
741 // Add the length to the length of white spaces at the end of the line.
742 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
746 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
747 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
749 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
751 // Clear the white space length at the end of the line.
752 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
755 if(isSplitToTwoHalves && (!isSecondHalf) &&
756 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
758 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
759 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
761 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
762 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
764 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
765 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
767 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
769 // Check if the accumulated length fits in the width of the box.
770 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
771 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
772 completelyFill && !isMultiline &&
773 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
775 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
777 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
779 GlyphMetrics glyphMetrics;
780 GetGlyphsMetrics(glyphIndexToRemove,
781 numberOfGLyphsInGroup,
786 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
787 lastGlyphOfParagraphPlusOne,
788 charactersPerGlyphBuffer);
790 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
791 const bool hasCharacters = charactersPerGlyph > 0u;
792 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
793 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
795 // Check whether is a white space.
796 const Character character = *(textBuffer + characterFirstIndex);
797 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
801 // Decrease the number of characters for SecondHalf.
802 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
804 // Decrease the number of glyphs for SecondHalf.
805 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
809 // Decrease the number of characters.
810 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
812 // Decrease the number of glyphs.
813 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
816 if(isRemovedGlyphWhiteSpace)
818 tmpLineLayout.penX -= glyphMetrics.advance;
819 tmpLineLayout.length -= glyphMetrics.advance;
823 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
824 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
829 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
830 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
831 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
835 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
836 tmpLineLayout.characterIndex = characterLastIndex + 1u;
837 glyphIndexToRemove = tmpLineLayout.glyphIndex;
841 else if((completelyFill || isMultiline) &&
842 (tmpLineLayout.length > targetWidth))
844 // Current word does not fit in the box's width.
845 if(((oneHyphenLaidOut && isHyphenMode) ||
846 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
849 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
850 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
853 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
855 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
857 // The word doesn't fit in the control's width. It needs to be split by character.
858 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
862 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
863 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
867 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
868 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
871 tmpLineLayout.penX = previousTmpPenX;
872 tmpLineLayout.previousAdvance = previousTmpAdvance;
873 tmpLineLayout.length = previousTmpLength;
874 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
877 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
879 // Add part of the word to the line layout and shift the first glyph.
880 MergeLineLayout(lineLayout, tmpLineLayout, true);
882 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
883 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
885 // Add part of the word to the line layout.
886 MergeLineLayout(lineLayout, tmpLineLayout, false);
891 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
894 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
896 // Reorder the RTL line.
897 if(bidiParameters.isBidirectional)
899 ReorderBiDiLayout(parameters,
904 enforceEllipsisInSingleLine);
910 if((isMultiline || isLastGlyph) &&
911 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
913 LineLayout currentLineLayout = lineLayout;
914 oneHyphenLaidOut = false;
916 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
918 // Must break the line. Update the line layout, shift the first glyph and return.
919 MergeLineLayout(lineLayout, tmpLineLayout, true);
923 // Must break the line. Update the line layout and return.
924 MergeLineLayout(lineLayout, tmpLineLayout, false);
927 // Reorder the RTL line.
928 if(bidiParameters.isBidirectional)
930 ReorderBiDiLayout(parameters,
935 enforceEllipsisInSingleLine);
938 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
939 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
945 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
947 oneHyphenLaidOut = false;
948 oneWordLaidOut = isWordLaidOut;
949 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
951 // Current glyph is the last one of the current word.
952 // Add the temporal layout to the current one.
953 MergeLineLayout(lineLayout, tmpLineLayout, false);
955 tmpLineLayout.Clear();
959 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
960 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
962 hyphenGlyph = GlyphInfo();
963 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
965 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
966 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
968 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
970 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
972 hyphenIndex = glyphIndex;
973 oneHyphenLaidOut = true;
975 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
977 // Current glyph is the last one of the current word hyphen.
978 // Add the temporal layout to the current one.
979 MergeLineLayout(lineLayout, tmpLineLayout, false);
981 tmpLineLayout.Clear();
985 glyphIndex += numberOfGLyphsInGroup;
988 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
991 void SetGlyphPositions(const Parameters& layoutParameters,
992 Vector2* glyphPositionsBuffer,
993 const LineLayout& layout)
995 // Traverse the glyphs and set the positions.
997 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
998 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
999 const Length numberOfGlyphs = layout.numberOfGlyphs;
1000 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1002 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1003 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1005 // Check if the x bearing of the first character is negative.
1006 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1007 // so the penX position needs to be moved to the right.
1008 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1009 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1011 for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
1013 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph + i);
1014 Vector2& position = *(glyphPositionsBuffer + startIndexForGlyphPositions + i);
1016 position.x = penX + glyph.xBearing;
1017 position.y = -glyph.yBearing;
1019 penX += (glyph.advance + interGlyphExtraAdvance);
1022 if(layout.isSplitToTwoHalves)
1024 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1025 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1026 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1028 for(GlyphIndex i = 0u; i < numberOfGlyphsInSecondHalfLine; ++i)
1030 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyphInSecondHalf + i);
1031 Vector2& position = *(glyphPositionsBuffer + startIndexForGlyphPositionsnSecondHalf + i);
1033 position.x = penX + glyph.xBearing;
1034 position.y = -glyph.yBearing;
1036 penX += (glyph.advance + interGlyphExtraAdvance);
1041 void SetGlyphPositions(const Parameters& layoutParameters,
1042 Vector2* glyphPositionsBuffer,
1043 LayoutBidiParameters& layoutBidiParameters,
1044 const LineLayout& layout)
1046 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
1047 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1048 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1049 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1050 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
1052 CharacterIndex characterLogicalIndex = 0u;
1053 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1054 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1058 if(layout.isSplitToTwoHalves)
1060 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
1062 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1063 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1065 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1067 position.y = -glyph.yBearing;
1069 penX += glyph.advance;
1071 ++characterLogicalIndex;
1072 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1076 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1078 extendedToSecondHalf = true;
1079 characterLogicalIndex = 0u;
1080 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1082 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
1084 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1085 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1087 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1089 position.y = -glyph.yBearing;
1091 penX += glyph.advance;
1093 ++characterLogicalIndex;
1094 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1098 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1099 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1101 penX += -glyph.xBearing;
1103 // Traverses the characters of the right to left paragraph.
1104 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1106 for(; characterLogicalIndex < bidiLine.characterRunForSecondHalfLine.numberOfCharacters;
1107 ++characterLogicalIndex)
1109 // Convert the character in the logical order into the character in the visual order.
1110 const CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1112 // Get the number of glyphs of the character.
1113 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
1115 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
1117 // Convert the character in the visual order into the glyph in the visual order.
1118 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
1120 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
1122 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1123 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1125 position.x = penX + glyph.xBearing;
1126 position.y = -glyph.yBearing;
1128 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
1133 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1134 for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1135 ++characterLogicalIndex)
1137 // Convert the character in the logical order into the character in the visual order.
1138 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1140 // Get the number of glyphs of the character.
1141 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
1143 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
1145 // Convert the character in the visual order into the glyph in the visual order.
1146 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
1148 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
1150 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1151 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
1153 position.x = penX + glyph.xBearing;
1154 position.y = -glyph.yBearing;
1156 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
1162 * @brief Resizes the line buffer.
1164 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1165 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1166 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1167 * @param[in] updateCurrentBuffer Whether the layout is updated.
1169 * @return Pointer to either lines or newLines.
1171 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1172 Vector<LineRun>& newLines,
1173 Length& linesCapacity,
1174 bool updateCurrentBuffer)
1176 LineRun* linesBuffer = nullptr;
1177 // Reserve more space for the next lines.
1178 linesCapacity *= 2u;
1179 if(updateCurrentBuffer)
1181 newLines.Resize(linesCapacity);
1182 linesBuffer = newLines.Begin();
1186 lines.Resize(linesCapacity);
1187 linesBuffer = lines.Begin();
1194 * Ellipsis a line if it exceeds the width's of the bounding box.
1196 * @param[in] layoutParameters The parameters needed to layout the text.
1197 * @param[in] layout The line layout.
1198 * @param[in,out] layoutSize The text's layout size.
1199 * @param[in,out] linesBuffer Pointer to the line's buffer.
1200 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1201 * @param[in,out] numberOfLines The number of laid-out lines.
1202 * @param[in] penY The vertical layout position.
1203 * @param[in] currentParagraphDirection The current paragraph's direction.
1204 * @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
1205 * @param[in] ellipsisPosition Where is the location the text elide
1207 * return Whether the line is ellipsized.
1209 bool EllipsisLine(const Parameters& layoutParameters,
1210 LayoutBidiParameters& layoutBidiParameters,
1211 const LineLayout& layout,
1213 LineRun* linesBuffer,
1214 Vector2* glyphPositionsBuffer,
1215 Length& numberOfLines,
1217 bool& isAutoScrollEnabled,
1218 DevelText::EllipsisPosition::Type ellipsisPosition,
1219 bool enforceEllipsisInSingleLine)
1221 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))));
1222 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1223 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1225 isAutoScrollEnabled = false;
1226 // Do not layout more lines if ellipsis is enabled.
1228 // The last line needs to be completely filled with characters.
1229 // Part of a word may be used.
1231 LineRun* lineRun = nullptr;
1232 LineLayout ellipsisLayout;
1233 if(0u != numberOfLines)
1235 // Get the last line and layout it again with the 'completelyFill' flag to true.
1236 lineRun = linesBuffer + (numberOfLines - 1u);
1237 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1239 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1243 // At least there is space reserved for one line.
1244 lineRun = linesBuffer;
1246 lineRun->glyphRun.glyphIndex = 0u;
1247 ellipsisLayout.glyphIndex = 0u;
1248 lineRun->isSplitToTwoHalves = false;
1253 GetLineLayoutForBox(layoutParameters,
1254 layoutBidiParameters,
1258 enforceEllipsisInSingleLine,
1261 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1263 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1266 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1267 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1268 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1269 lineRun->width = ellipsisLayout.length;
1270 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1271 lineRun->ascender = ellipsisLayout.ascender;
1272 lineRun->descender = ellipsisLayout.descender;
1273 lineRun->ellipsis = true;
1275 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1276 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1277 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1278 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1279 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1281 layoutSize.width = layoutParameters.boundingBox.width;
1282 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1284 layoutSize.height += GetLineHeight(*lineRun);
1287 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1289 if(layoutBidiParameters.isBidirectional)
1291 layoutBidiParameters.bidiLineIndex = 0u;
1292 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1293 endIt = bidirectionalLinesInfo.End();
1295 ++it, ++layoutBidiParameters.bidiLineIndex)
1297 const BidirectionalLineInfoRun& run = *it;
1298 //To handle case when the laid characters exist in next line.
1299 //More than one BidirectionalLineInfoRun could start with same character.
1300 //When need to check also numberOfCharacters in line.
1301 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1302 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1303 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1304 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1305 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1307 // Found where to insert the bidi line info.
1313 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1315 if((nullptr != bidirectionalLineInfo) &&
1316 !bidirectionalLineInfo->isIdentity &&
1317 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1319 lineRun->direction = RTL;
1320 SetGlyphPositions(layoutParameters,
1321 glyphPositionsBuffer,
1322 layoutBidiParameters,
1327 lineRun->direction = LTR;
1328 SetGlyphPositions(layoutParameters,
1329 glyphPositionsBuffer,
1338 * @brief Updates the text layout with a new laid-out line.
1340 * @param[in] layoutParameters The parameters needed to layout the text.
1341 * @param[in] layout The line layout.
1342 * @param[in,out] layoutSize The text's layout size.
1343 * @param[in,out] linesBuffer Pointer to the line's buffer.
1344 * @param[in] index Index to the vector of glyphs.
1345 * @param[in,out] numberOfLines The number of laid-out lines.
1346 * @param[in] isLastLine Whether the laid-out line is the last one.
1348 void UpdateTextLayout(const Parameters& layoutParameters,
1349 const LineLayout& layout,
1351 LineRun* linesBuffer,
1353 Length& numberOfLines,
1356 LineRun& lineRun = *(linesBuffer + numberOfLines);
1359 lineRun.glyphRun.glyphIndex = index;
1360 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1361 lineRun.characterRun.characterIndex = layout.characterIndex;
1362 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1363 lineRun.width = layout.length;
1364 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1366 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1367 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1368 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1369 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1370 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1372 // Rounds upward to avoid a non integer size.
1373 lineRun.width = std::ceil(lineRun.width);
1375 lineRun.ascender = layout.ascender;
1376 lineRun.descender = layout.descender;
1377 lineRun.direction = layout.direction;
1378 lineRun.ellipsis = false;
1380 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1381 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1383 lineRun.lineSpacing += mDefaultLineSpacing;
1385 // Update the actual size.
1386 if(lineRun.width > layoutSize.width)
1388 layoutSize.width = lineRun.width;
1391 layoutSize.height += GetLineHeight(lineRun);
1395 * @brief Updates the text layout with the last laid-out line.
1397 * @param[in] layoutParameters The parameters needed to layout the text.
1398 * @param[in] characterIndex The character index of the line.
1399 * @param[in] glyphIndex The glyph index of the line.
1400 * @param[in,out] layoutSize The text's layout size.
1401 * @param[in,out] linesBuffer Pointer to the line's buffer.
1402 * @param[in,out] numberOfLines The number of laid-out lines.
1404 void UpdateTextLayout(const Parameters& layoutParameters,
1405 CharacterIndex characterIndex,
1406 GlyphIndex glyphIndex,
1408 LineRun* linesBuffer,
1409 Length& numberOfLines)
1411 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1413 // Need to add a new line with no characters but with height to increase the layoutSize.height
1414 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1416 Text::FontMetrics fontMetrics;
1417 if(0u != glyphInfo.fontId)
1419 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1422 LineRun& lineRun = *(linesBuffer + numberOfLines);
1425 lineRun.glyphRun.glyphIndex = glyphIndex;
1426 lineRun.glyphRun.numberOfGlyphs = 0u;
1427 lineRun.characterRun.characterIndex = characterIndex;
1428 lineRun.characterRun.numberOfCharacters = 0u;
1429 lineRun.width = 0.f;
1430 lineRun.ascender = fontMetrics.ascender;
1431 lineRun.descender = fontMetrics.descender;
1432 lineRun.extraLength = 0.f;
1433 lineRun.alignmentOffset = 0.f;
1434 lineRun.direction = LTR;
1435 lineRun.ellipsis = false;
1437 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1438 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1440 lineRun.lineSpacing += mDefaultLineSpacing;
1442 layoutSize.height += GetLineHeight(lineRun);
1446 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1448 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1449 * @param[in,out] layoutSize The text's layout size.
1451 void UpdateLayoutSize(const Vector<LineRun>& lines,
1454 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1455 endIt = lines.End();
1459 const LineRun& line = *it;
1461 if(line.width > layoutSize.width)
1463 layoutSize.width = line.width;
1466 layoutSize.height += GetLineHeight(line);
1471 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1473 * @param[in] layoutParameters The parameters needed to layout the text.
1474 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1475 * @param[in] characterOffset The offset to be added to the runs of characters.
1476 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1478 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1479 Vector<LineRun>& lines,
1480 Length characterOffset,
1483 // Update the glyph and character runs.
1484 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1485 endIt = lines.End();
1489 LineRun& line = *it;
1491 line.glyphRun.glyphIndex = glyphOffset;
1492 line.characterRun.characterIndex = characterOffset;
1494 glyphOffset += line.glyphRun.numberOfGlyphs;
1495 characterOffset += line.characterRun.numberOfCharacters;
1499 bool LayoutText(Parameters& layoutParameters,
1501 bool elideTextEnabled,
1502 bool& isAutoScrollEnabled,
1503 DevelText::EllipsisPosition::Type ellipsisPosition)
1505 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1506 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1508 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1509 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1511 //Reset indices of ElidedGlyphs
1512 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1513 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1514 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1515 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1517 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1519 if(0u == layoutParameters.numberOfGlyphs)
1521 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1522 if(layoutParameters.isLastNewParagraph)
1524 Length numberOfLines = lines.Count();
1525 if(0u != numberOfLines)
1527 const LineRun& lastLine = *(lines.End() - 1u);
1529 if(0u != lastLine.characterRun.numberOfCharacters)
1531 // Need to add a new line with no characters but with height to increase the layoutSize.height
1533 Initialize(newLine);
1534 lines.PushBack(newLine);
1536 UpdateTextLayout(layoutParameters,
1537 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1538 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1546 // Calculates the layout size.
1547 UpdateLayoutSize(lines,
1550 // Rounds upward to avoid a non integer size.
1551 layoutSize.height = std::ceil(layoutSize.height);
1553 // Nothing else do if there are no glyphs to layout.
1557 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1558 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1559 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1561 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1562 // This extra line needs to be removed.
1563 if(0u != lines.Count())
1565 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1567 if((0u == lastLine->characterRun.numberOfCharacters) &&
1568 (lastGlyphPlusOne == totalNumberOfGlyphs))
1570 lines.Remove(lastLine);
1574 // Retrieve BiDi info.
1575 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1577 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1578 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1579 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1581 // Set the layout bidirectional paramters.
1582 LayoutBidiParameters layoutBidiParameters;
1584 // Whether the layout is being updated or set from scratch.
1585 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1587 Vector2* glyphPositionsBuffer = nullptr;
1588 Vector<Vector2> newGlyphPositions;
1590 LineRun* linesBuffer = nullptr;
1591 Vector<LineRun> newLines;
1593 // Estimate the number of lines.
1594 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1595 Length numberOfLines = 0u;
1597 if(updateCurrentBuffer)
1599 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1600 glyphPositionsBuffer = newGlyphPositions.Begin();
1602 newLines.Resize(linesCapacity);
1603 linesBuffer = newLines.Begin();
1607 glyphPositionsBuffer = glyphPositions.Begin();
1609 lines.Resize(linesCapacity);
1610 linesBuffer = lines.Begin();
1613 float penY = CalculateLineOffset(lines,
1614 layoutParameters.startLineIndex);
1615 bool anyLineIsEliped = false;
1616 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1618 layoutBidiParameters.Clear();
1620 if(hasBidiParagraphs)
1622 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1624 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1625 endIt = bidirectionalParagraphsInfo.End();
1627 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1629 const BidirectionalParagraphInfoRun& run = *it;
1631 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1633 if(lastCharacterIndex <= startCharacterIndex)
1635 // Do not process, the paragraph has already been processed.
1639 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1641 layoutBidiParameters.paragraphDirection = run.direction;
1642 layoutBidiParameters.isBidirectional = true;
1645 // Has already been found.
1649 if(layoutBidiParameters.isBidirectional)
1651 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1652 endIt = bidirectionalLinesInfo.End();
1654 ++it, ++layoutBidiParameters.bidiLineIndex)
1656 const BidirectionalLineInfoRun& run = *it;
1658 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1660 if(lastCharacterIndex <= startCharacterIndex)
1666 if(startCharacterIndex < lastCharacterIndex)
1668 // Found where to insert the bidi line info.
1675 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1677 // Get the layout for the line.
1679 layout.direction = layoutBidiParameters.paragraphDirection;
1680 layout.glyphIndex = index;
1681 GetLineLayoutForBox(layoutParameters,
1682 layoutBidiParameters,
1689 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1690 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1691 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1692 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1693 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1695 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1697 // The width is too small and no characters are laid-out.
1698 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1700 lines.Resize(numberOfLines);
1702 // Rounds upward to avoid a non integer size.
1703 layoutSize.height = std::ceil(layoutSize.height);
1708 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1710 penY += layout.ascender;
1712 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1714 bool ellipsis = false;
1715 if(elideTextEnabled)
1717 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1719 // Does the ellipsis of the last line.
1720 ellipsis = EllipsisLine(layoutParameters,
1721 layoutBidiParameters,
1725 glyphPositionsBuffer,
1728 isAutoScrollEnabled,
1733 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1735 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1736 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1738 ellipsis = EllipsisLine(layoutParameters,
1739 layoutBidiParameters,
1743 glyphPositionsBuffer,
1746 isAutoScrollEnabled,
1751 //clear hyphen from ellipsis line
1752 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1753 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1755 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1757 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1758 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1762 // No more lines to layout.
1767 //In START location of ellipsis whether to shift lines or not.
1768 anyLineIsEliped |= ellipsis;
1770 // Whether the last line has been laid-out.
1771 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1773 if(numberOfLines == linesCapacity)
1775 // Reserve more space for the next lines.
1776 linesBuffer = ResizeLinesBuffer(lines,
1779 updateCurrentBuffer);
1782 // Updates the current text's layout with the line's layout.
1783 UpdateTextLayout(layoutParameters,
1791 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1793 if((nextIndex == totalNumberOfGlyphs) &&
1794 layoutParameters.isLastNewParagraph &&
1795 (mLayout == MULTI_LINE_BOX))
1797 // The last character of the text is a new paragraph character.
1798 // An extra line with no characters is added to increase the text's height
1799 // in order to place the cursor.
1801 if(numberOfLines == linesCapacity)
1803 // Reserve more space for the next lines.
1804 linesBuffer = ResizeLinesBuffer(lines,
1807 updateCurrentBuffer);
1810 UpdateTextLayout(layoutParameters,
1811 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1812 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1816 } // whether to add a last line.
1818 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1820 if((nullptr != bidirectionalLineInfo) &&
1821 !bidirectionalLineInfo->isIdentity &&
1822 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1824 SetGlyphPositions(layoutParameters,
1825 glyphPositionsBuffer,
1826 layoutBidiParameters,
1831 // Sets the positions of the glyphs.
1832 SetGlyphPositions(layoutParameters,
1833 glyphPositionsBuffer,
1837 // Updates the vertical pen's position.
1838 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1839 // If there is a defaultLineSize, updates the pen's position.
1840 if(mDefaultLineSize > 0.f)
1842 float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1843 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1844 penY += lineSpacing;
1847 // Increase the glyph index.
1850 } // end for() traversing glyphs.
1852 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1853 if(anyLineIsEliped && numberOfLines > 1u)
1855 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1857 Length lineIndex = 0;
1858 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1860 LineRun& delLine = linesBuffer[lineIndex];
1861 delLine.ellipsis = true;
1863 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1864 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1866 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1867 linesBuffer[lineIndex].ellipsis = false;
1872 linesBuffer[0u].ellipsis = true;
1874 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1876 Length middleLineIndex = (numberOfLines) / 2u;
1877 Length ellipsisLineIndex = 0u;
1878 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1880 LineRun& delLine = linesBuffer[middleLineIndex];
1881 delLine.ellipsis = true;
1883 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1884 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1886 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1887 linesBuffer[lineIndex].ellipsis = false;
1890 ellipsisLineIndex = middleLineIndex - 1u;
1891 middleLineIndex = (numberOfLines) / 2u;
1894 linesBuffer[ellipsisLineIndex].ellipsis = true;
1898 if(updateCurrentBuffer)
1900 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1901 newGlyphPositions.Begin(),
1902 newGlyphPositions.End());
1903 glyphPositions.Resize(totalNumberOfGlyphs);
1905 newLines.Resize(numberOfLines);
1907 // Current text's layout size adds only the newly laid-out lines.
1908 // Updates the layout size with the previously laid-out lines.
1909 UpdateLayoutSize(lines,
1912 if(0u != newLines.Count())
1914 const LineRun& lastLine = *(newLines.End() - 1u);
1916 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1917 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1919 // Update the indices of the runs before the new laid-out lines are inserted.
1920 UpdateLineIndexOffsets(layoutParameters,
1925 // Insert the lines.
1926 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1933 lines.Resize(numberOfLines);
1936 // Rounds upward to avoid a non integer size.
1937 layoutSize.height = std::ceil(layoutSize.height);
1939 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1944 void Align(const Size& size,
1945 CharacterIndex startIndex,
1946 Length numberOfCharacters,
1947 Text::HorizontalAlignment::Type horizontalAlignment,
1948 Vector<LineRun>& lines,
1949 float& alignmentOffset,
1950 Dali::LayoutDirection::Type layoutDirection,
1951 bool matchLayoutDirection)
1953 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1955 alignmentOffset = MAX_FLOAT;
1956 // Traverse all lines and align the glyphs.
1957 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1961 LineRun& line = *it;
1963 if(line.characterRun.characterIndex < startIndex)
1965 // Do not align lines which have already been aligned.
1969 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1971 // Do not align lines beyond the last laid-out character.
1975 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1977 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1981 // Calculate the line's alignment offset accordingly with the align option,
1982 // the box width, line length, and the paragraph's direction.
1983 CalculateHorizontalAlignment(size.width,
1984 horizontalAlignment,
1987 matchLayoutDirection);
1989 // Updates the alignment offset.
1990 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1994 void CalculateHorizontalAlignment(float boxWidth,
1995 HorizontalAlignment::Type horizontalAlignment,
1997 Dali::LayoutDirection::Type layoutDirection,
1998 bool matchLayoutDirection)
2000 line.alignmentOffset = 0.f;
2001 const bool isLineRTL = RTL == line.direction;
2003 // Whether to swap the alignment.
2004 // 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.
2005 bool isLayoutRTL = isLineRTL;
2006 float lineLength = line.width;
2008 // match align for system language direction
2009 if(matchLayoutDirection)
2011 // Swap the alignment type if the line is right to left.
2012 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2014 // Calculate the horizontal line offset.
2015 switch(horizontalAlignment)
2017 case HorizontalAlignment::BEGIN:
2023 lineLength += line.extraLength;
2026 line.alignmentOffset = boxWidth - lineLength;
2030 line.alignmentOffset = 0.f;
2034 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2035 line.alignmentOffset -= line.extraLength;
2040 case HorizontalAlignment::CENTER:
2042 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2046 line.alignmentOffset -= line.extraLength;
2049 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2052 case HorizontalAlignment::END:
2056 line.alignmentOffset = 0.f;
2060 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2061 line.alignmentOffset -= line.extraLength;
2068 lineLength += line.extraLength;
2071 line.alignmentOffset = boxWidth - lineLength;
2078 void Initialize(LineRun& line)
2080 line.glyphRun.glyphIndex = 0u;
2081 line.glyphRun.numberOfGlyphs = 0u;
2082 line.characterRun.characterIndex = 0u;
2083 line.characterRun.numberOfCharacters = 0u;
2085 line.ascender = 0.f;
2086 line.descender = 0.f;
2087 line.extraLength = 0.f;
2088 line.alignmentOffset = 0.f;
2089 line.direction = LTR;
2090 line.ellipsis = false;
2091 line.lineSpacing = mDefaultLineSpacing;
2092 line.isSplitToTwoHalves = false;
2093 line.glyphRunSecondHalf.glyphIndex = 0u;
2094 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2095 line.characterRunForSecondHalfLine.characterIndex = 0u;
2096 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2101 float mDefaultLineSpacing;
2102 float mDefaultLineSize;
2104 IntrusivePtr<Metrics> mMetrics;
2110 mImpl = new Engine::Impl();
2118 void Engine::SetMetrics(MetricsPtr& metrics)
2120 mImpl->mMetrics = metrics;
2123 void Engine::SetLayout(Type layout)
2125 mImpl->mLayout = layout;
2128 Engine::Type Engine::GetLayout() const
2130 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2131 return mImpl->mLayout;
2134 void Engine::SetCursorWidth(int width)
2136 mImpl->mCursorWidth = static_cast<float>(width);
2139 int Engine::GetCursorWidth() const
2141 return static_cast<int>(mImpl->mCursorWidth);
2144 bool Engine::LayoutText(Parameters& layoutParameters,
2146 bool elideTextEnabled,
2147 bool& isAutoScrollEnabled,
2148 DevelText::EllipsisPosition::Type ellipsisPosition)
2150 return mImpl->LayoutText(layoutParameters,
2153 isAutoScrollEnabled,
2157 void Engine::Align(const Size& size,
2158 CharacterIndex startIndex,
2159 Length numberOfCharacters,
2160 Text::HorizontalAlignment::Type horizontalAlignment,
2161 Vector<LineRun>& lines,
2162 float& alignmentOffset,
2163 Dali::LayoutDirection::Type layoutDirection,
2164 bool matchLayoutDirection)
2169 horizontalAlignment,
2173 matchLayoutDirection);
2176 void Engine::SetDefaultLineSpacing(float lineSpacing)
2178 mImpl->mDefaultLineSpacing = lineSpacing;
2181 float Engine::GetDefaultLineSpacing() const
2183 return mImpl->mDefaultLineSpacing;
2186 void Engine::SetDefaultLineSize(float lineSize)
2188 mImpl->mDefaultLineSize = lineSize;
2191 float Engine::GetDefaultLineSize() const
2193 return mImpl->mDefaultLineSize;
2196 } // namespace Layout
2200 } // namespace Toolkit