2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-engine-helper-functions.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
33 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
41 float GetLineHeight(const LineRun lineRun, bool isLastLine)
43 // The line height is the addition of the line ascender, the line descender and the line spacing.
44 // However, the line descender has a negative value, hence the subtraction.
45 // In case this is the only/last line then line spacing should be ignored.
46 float lineHeight = lineRun.ascender - lineRun.descender;
48 if(!isLastLine || lineRun.lineSpacing > 0)
50 lineHeight += lineRun.lineSpacing;
59 #if defined(DEBUG_ENABLED)
60 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
63 const float MAX_FLOAT = std::numeric_limits<float>::max();
64 const CharacterDirection LTR = false;
65 const CharacterDirection RTL = !LTR;
66 const float LINE_SPACING = 0.f;
67 const float MIN_LINE_SIZE = 0.f;
68 const Character HYPHEN_UNICODE = 0x002D;
69 const float RELATIVE_LINE_SIZE = 1.f;
71 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
73 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
79 * @brief Stores temporary layout info of the line.
87 numberOfCharacters{0u},
94 whiteSpaceLengthEndOfLine{0.f},
96 isSplitToTwoHalves(false),
97 glyphIndexInSecondHalfLine{0u},
98 characterIndexInSecondHalfLine{0u},
99 numberOfGlyphsInSecondHalfLine{0u},
100 numberOfCharactersInSecondHalfLine{0u}
114 numberOfCharacters = 0u;
115 ascender = -MAX_FLOAT;
116 descender = MAX_FLOAT;
118 isSplitToTwoHalves = false;
119 glyphIndexInSecondHalfLine = 0u;
120 characterIndexInSecondHalfLine = 0u;
121 numberOfGlyphsInSecondHalfLine = 0u;
122 numberOfCharactersInSecondHalfLine = 0u;
125 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
126 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
127 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
128 Length numberOfCharacters; ///< The number of characters which fit in one line.
129 float ascender; ///< The maximum ascender of all fonts in the line.
130 float descender; ///< The minimum descender of all fonts in the line.
131 float lineSpacing; ///< The line spacing
132 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
133 float previousAdvance; ///< The advance of the previous glyph.
134 float length; ///< The current length of the line.
135 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
136 CharacterDirection direction;
138 bool isSplitToTwoHalves; ///< Whether the second half is defined.
139 GlyphIndex glyphIndexInSecondHalfLine; ///< Index of the first glyph to be laid-out for the second half of line.
140 CharacterIndex characterIndexInSecondHalfLine; ///< Index of the first character to be laid-out for the second half of line.
141 Length numberOfGlyphsInSecondHalfLine; ///< The number of glyph which fit in one line for the second half of line.
142 Length numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
145 struct LayoutBidiParameters
149 paragraphDirection = LTR;
150 bidiParagraphIndex = 0u;
152 isBidirectional = false;
155 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
156 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
157 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
158 bool isBidirectional = false; ///< Whether the text is bidirectional.
164 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
166 mDefaultLineSpacing{LINE_SPACING},
167 mDefaultLineSize{MIN_LINE_SIZE},
168 mRelativeLineSize{RELATIVE_LINE_SIZE}
173 * @brief get the line spacing.
175 * @param[in] textSize The text size.
176 * @return the line spacing value.
178 float GetLineSpacing(float textSize)
183 // Sets the line size
184 lineSpacing = mDefaultLineSize - textSize;
185 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
187 // Add the line spacing
188 lineSpacing += mDefaultLineSpacing;
190 //subtract line spcaing if relativeLineSize < 1 & larger than min height
191 relTextSize = textSize * mRelativeLineSize;
192 if(relTextSize > mDefaultLineSize)
194 if(mRelativeLineSize < 1)
196 //subtract the difference (always will be positive)
197 lineSpacing -= (textSize - relTextSize);
201 //reverse the addition in the top.
202 if(mDefaultLineSize > textSize)
204 lineSpacing -= mDefaultLineSize - textSize;
207 //add difference instead
208 lineSpacing += relTextSize - textSize;
216 * @brief Updates the line ascender and descender with the metrics of a new font.
218 * @param[in] glyphMetrics The metrics of the new font.
219 * @param[in,out] lineLayout The line layout.
221 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
223 Text::FontMetrics fontMetrics;
224 if(0u != glyphMetrics.fontId)
226 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
230 fontMetrics.ascender = glyphMetrics.fontHeight;
231 fontMetrics.descender = 0.f;
232 fontMetrics.height = fontMetrics.ascender;
233 fontMetrics.underlinePosition = 0.f;
234 fontMetrics.underlineThickness = 1.f;
237 // Sets the maximum ascender.
238 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
240 // Sets the minimum descender.
241 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
243 lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
247 * @brief Merges a temporary line layout into the line layout.
249 * @param[in,out] lineLayout The line layout.
250 * @param[in] tmpLineLayout A temporary line layout.
251 * @param[in] isShifted Whether to shift first glyph and character indices.
253 void MergeLineLayout(LineLayout& lineLayout,
254 const LineLayout& tmpLineLayout,
257 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
258 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
260 lineLayout.penX = tmpLineLayout.penX;
261 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
263 lineLayout.length = tmpLineLayout.length;
264 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
266 // Sets the maximum ascender.
267 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
269 // Sets the minimum descender.
270 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
272 // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
275 lineLayout.glyphIndex = tmpLineLayout.glyphIndex;
276 lineLayout.characterIndex = tmpLineLayout.characterIndex;
279 lineLayout.isSplitToTwoHalves = tmpLineLayout.isSplitToTwoHalves;
280 lineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndexInSecondHalfLine;
281 lineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndexInSecondHalfLine;
282 lineLayout.numberOfGlyphsInSecondHalfLine = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
283 lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
286 void LayoutRightToLeft(const Parameters& parameters,
287 const BidirectionalLineInfoRun& bidirectionalLineInfo,
289 float& whiteSpaceLengthEndOfLine)
291 // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
292 // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
293 // then the first half of line "characterRun",
294 // Otherwise travers whole characters in"characterRun".
296 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
297 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
298 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
299 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
301 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
302 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
303 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
305 // Get the character-spacing runs.
306 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
308 CharacterIndex characterLogicalIndex = 0u;
309 CharacterIndex characterVisualIndex = 0u;
311 float calculatedAdvance = 0.f;
313 // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
314 // Otherwise maps the first visual index from visualToLogicalMap.
315 // This is to initialize the first visual index.
316 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
318 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
322 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
325 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
327 if(RTL == bidirectionalLineInfo.direction)
329 // If there are characters in the second half of Line.
330 if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
332 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
333 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
335 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
337 const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
338 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
339 whiteSpaceLengthEndOfLine += calculatedAdvance;
341 ++characterLogicalIndex;
342 characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
346 // If all characters in the second half of Line are WhiteSpaces.
347 // then continue adding the WhiteSpaces from the first hel of Line.
348 // Also this is valid when the line was not splitted.
349 if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
351 extendedToSecondHalf = true; // Whether the logical index is extended to second half
352 characterLogicalIndex = 0u;
353 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
355 // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
356 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
358 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
360 const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
361 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
362 whiteSpaceLengthEndOfLine += calculatedAdvance;
364 ++characterLogicalIndex;
365 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
370 // Here's the first index of character is not WhiteSpace
371 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
373 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
374 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
375 lastGlyphOfParagraphPlusOne,
376 charactersPerGlyphBuffer);
378 GlyphMetrics glyphMetrics;
379 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
380 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
381 GetGlyphsMetrics(glyphIndex,
382 numberOfGLyphsInGroup,
388 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
390 // Traverses the characters of the right to left paragraph.
391 // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
392 if(!extendedToSecondHalf &&
393 bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
395 for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
397 // Convert the character in the logical order into the character in the visual order.
398 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
399 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
401 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
403 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
404 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
405 lastGlyphOfParagraphPlusOne,
406 charactersPerGlyphBuffer);
408 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
410 GlyphMetrics glyphMetrics;
411 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
412 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
413 GetGlyphsMetrics(glyphIndex,
414 numberOfGLyphsInGroup,
422 // If glyph is WhiteSpace then:
423 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
424 // the endOfLine in RTL was the headOfLine for layouting.
425 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
426 if(RTL == bidirectionalLineInfo.direction)
428 length += glyphMetrics.advance;
432 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
434 penX += glyphMetrics.advance;
438 // If glyph is not whiteSpace then:
439 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
440 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
441 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
442 // Otherwise the current length is maximum.
443 if(LTR == bidirectionalLineInfo.direction)
445 whiteSpaceLengthEndOfLine = 0.f;
447 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
448 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
453 // Continue traversing in the first half of line or in the whole line.
454 // If the second half of line was extended then continue from logical index in the first half of line
455 // Also this is valid when the line was not splitted and there were WhiteSpace.
456 // Otherwise start from first logical index in line.
457 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
458 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
460 // Convert the character in the logical order into the character in the visual order.
461 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
462 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
464 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
466 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
467 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
468 lastGlyphOfParagraphPlusOne,
469 charactersPerGlyphBuffer);
471 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
473 GlyphMetrics glyphMetrics;
474 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
475 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
476 GetGlyphsMetrics(glyphIndex,
477 numberOfGLyphsInGroup,
485 // If glyph is WhiteSpace then:
486 // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
487 // the endOfLine in RTL was the headOfLine for layouting.
488 // But for LTR added it to the endOfLine and use "advance" to accumulate length.
489 if(RTL == bidirectionalLineInfo.direction)
491 length += glyphMetrics.advance;
495 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
497 penX += glyphMetrics.advance;
501 // If glyph is not whiteSpace then:
502 // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
503 // Use "advance" and "interGlyphExtraAdvance" to shift penX.
504 // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
505 // Otherwise the current length is maximum.
506 if(LTR == bidirectionalLineInfo.direction)
508 whiteSpaceLengthEndOfLine = 0.f;
510 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
511 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
516 void ReorderBiDiLayout(const Parameters& parameters,
517 LayoutBidiParameters& bidiParameters,
518 const LineLayout& currentLineLayout,
519 LineLayout& lineLayout,
520 bool breakInCharacters,
521 bool enforceEllipsisInSingleLine)
523 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
525 // The last glyph to be laid-out.
526 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
528 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
530 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
531 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
532 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
534 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
536 // Sets the visual to logical map tables needed to reorder the text.
537 ReorderLine(bidirectionalParagraphInfo,
538 bidirectionalLinesInfo,
539 bidiParameters.bidiLineIndex,
540 lineLayout.characterIndex,
541 lineLayout.numberOfCharacters,
542 lineLayout.characterIndexInSecondHalfLine,
543 lineLayout.numberOfCharactersInSecondHalfLine,
544 bidiParameters.paragraphDirection);
546 // Recalculate the length of the line and update the layout.
547 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
549 if(!bidirectionalLineInfo.isIdentity)
552 float whiteSpaceLengthEndOfLine = 0.f;
553 LayoutRightToLeft(parameters,
554 bidirectionalLineInfo,
556 whiteSpaceLengthEndOfLine);
558 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
559 if(!Equals(length, lineLayout.length))
561 const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
563 if(isMultiline && (length > parameters.boundingBox.width))
565 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
567 // The word doesn't fit in one line. It has to be split by character.
569 // Remove the last laid out glyph(s) as they doesn't fit.
570 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
572 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
573 lastGlyphOfParagraphPlusOne,
574 charactersPerGlyphBuffer);
576 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
578 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
579 lineLayout.numberOfCharacters -= numberOfCharacters;
581 AdjustLayout(parameters,
583 bidirectionalParagraphInfo,
586 if(lineLayout.length < parameters.boundingBox.width)
591 if(glyphIndex < numberOfGLyphsInGroup)
593 // avoids go under zero for an unsigned int.
597 glyphIndex -= numberOfGLyphsInGroup;
602 lineLayout = currentLineLayout;
604 AdjustLayout(parameters,
606 bidirectionalParagraphInfo,
612 lineLayout.length = std::max(length, lineLayout.length);
619 void AdjustLayout(const Parameters& parameters,
620 LayoutBidiParameters& bidiParameters,
621 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
622 LineLayout& lineLayout)
624 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
626 // Remove current reordered line.
627 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
629 // Re-build the conversion table without the removed glyphs.
630 ReorderLine(bidirectionalParagraphInfo,
631 bidirectionalLinesInfo,
632 bidiParameters.bidiLineIndex,
633 lineLayout.characterIndex,
634 lineLayout.numberOfCharacters,
635 lineLayout.characterIndexInSecondHalfLine,
636 lineLayout.numberOfCharactersInSecondHalfLine,
637 bidiParameters.paragraphDirection);
639 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
642 float whiteSpaceLengthEndOfLine = 0.f;
643 LayoutRightToLeft(parameters,
644 bidirectionalLineInfo,
646 whiteSpaceLengthEndOfLine);
648 lineLayout.length = length;
649 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
653 * Retrieves the line layout for a given box width.
655 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
656 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
657 * to reorder the line and recalculate its length.
660 * @param[in] parameters The layout parameters.
661 * @param[] bidiParameters Bidirectional info for the current line.
662 * @param[out] lineLayout The line layout.
663 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
664 * @param[in] ellipsisPosition Where is the location the text elide
666 void GetLineLayoutForBox(const Parameters& parameters,
667 LayoutBidiParameters& bidiParameters,
668 LineLayout& lineLayout,
670 DevelText::EllipsisPosition::Type ellipsisPosition,
671 bool enforceEllipsisInSingleLine,
672 bool elideTextEnabled)
674 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
675 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
677 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
678 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
679 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
680 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
681 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
683 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
684 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
686 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
687 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
688 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
689 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
690 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
691 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
693 const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
695 // The last glyph to be laid-out.
696 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
698 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
699 // In the case the line starts with a right to left character, if the width is longer than the advance,
700 // the difference needs to be added to the line length.
702 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
703 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
704 lastGlyphOfParagraphPlusOne,
705 charactersPerGlyphBuffer);
707 float targetWidth = parameters.boundingBox.width;
708 float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
710 bool isSecondHalf = false;
712 const float modelCharacterSpacing = parameters.textModel->mVisualModel->GetCharacterSpacing();
713 float calculatedAdvance = 0.f;
714 Vector<CharacterIndex>& glyphToCharacterMap = parameters.textModel->mVisualModel->mGlyphsToCharacters;
715 const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
717 // Get the character-spacing runs.
718 const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
720 GlyphMetrics glyphMetrics;
721 const float characterSpacing = GetGlyphCharacterSpacing(lineLayout.glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
722 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
723 GetGlyphsMetrics(lineLayout.glyphIndex,
724 numberOfGLyphsInGroup,
730 // Set the direction of the first character of the line.
731 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
733 // Stores temporary line layout which has not been added to the final line layout.
734 LineLayout tmpLineLayout;
736 // Initialize the start point.
738 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
739 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
740 // 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.
741 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
743 // Calculate the line height if there is no characters.
744 FontId lastFontId = glyphMetrics.fontId;
745 UpdateLineHeight(glyphMetrics, tmpLineLayout);
747 bool oneWordLaidOut = false;
748 bool oneHyphenLaidOut = false;
749 GlyphIndex hyphenIndex = 0;
750 GlyphInfo hyphenGlyph;
752 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
753 glyphIndex < lastGlyphOfParagraphPlusOne;)
755 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
757 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
758 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
759 lastGlyphOfParagraphPlusOne,
760 charactersPerGlyphBuffer);
762 GlyphMetrics glyphMetrics;
763 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
764 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
765 GetGlyphsMetrics(glyphIndex,
766 numberOfGLyphsInGroup,
772 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
774 // Check if the font of the current glyph is the same of the previous one.
775 // If it's different the ascender and descender need to be updated.
776 if(lastFontId != glyphMetrics.fontId)
778 UpdateLineHeight(glyphMetrics, tmpLineLayout);
779 lastFontId = glyphMetrics.fontId;
782 // Get the character indices for the current glyph. The last character index is needed
783 // because there are glyphs formed by more than one character but their break info is
784 // given only for the last character.
785 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
786 const bool hasCharacters = charactersPerGlyph > 0u;
787 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
788 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
790 // Get the line break info for the current character.
791 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
795 // Increase the number of characters.
796 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
798 // Increase the number of glyphs.
799 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
803 // Increase the number of characters.
804 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
806 // Increase the number of glyphs.
807 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
810 // Check whether is a white space.
811 const Character character = *(textBuffer + characterFirstIndex);
812 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
814 // Calculate the length of the line.
816 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
817 const float previousTmpPenX = tmpLineLayout.penX;
818 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
819 const float previousTmpLength = tmpLineLayout.length;
820 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
824 // Add the length to the length of white spaces at the end of the line.
825 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
826 // The advance is used as the width is always zero for the white spaces.
830 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
831 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
833 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
835 // Clear the white space length at the end of the line.
836 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
839 if(isSplitToTwoHalves && (!isSecondHalf) &&
840 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
842 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
843 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
845 tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
846 tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
848 tmpLineLayout.glyphIndexInSecondHalfLine = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
849 tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
851 tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
853 // Check if the accumulated length fits in the width of the box.
854 if((ellipsisPosition == DevelText::EllipsisPosition::START ||
855 (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
856 completelyFill && !isMultiline &&
857 (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
859 GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
861 while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
863 GlyphMetrics glyphMetrics;
864 const float characterSpacing = GetGlyphCharacterSpacing(glyphIndexToRemove, characterSpacingGlyphRuns, modelCharacterSpacing);
865 calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
866 GetGlyphsMetrics(glyphIndexToRemove,
867 numberOfGLyphsInGroup,
873 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
874 lastGlyphOfParagraphPlusOne,
875 charactersPerGlyphBuffer);
877 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
878 const bool hasCharacters = charactersPerGlyph > 0u;
879 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
880 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
882 // Check whether is a white space.
883 const Character character = *(textBuffer + characterFirstIndex);
884 const bool isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
888 // Decrease the number of characters for SecondHalf.
889 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
891 // Decrease the number of glyphs for SecondHalf.
892 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
896 // Decrease the number of characters.
897 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
899 // Decrease the number of glyphs.
900 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
903 if(isRemovedGlyphWhiteSpace)
905 tmpLineLayout.penX -= glyphMetrics.advance;
906 tmpLineLayout.length -= glyphMetrics.advance;
910 tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
911 tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
916 tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
917 tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
918 glyphIndexToRemove = tmpLineLayout.glyphIndexInSecondHalfLine;
922 tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
923 tmpLineLayout.characterIndex = characterLastIndex + 1u;
924 glyphIndexToRemove = tmpLineLayout.glyphIndex;
928 else if((completelyFill || isMultiline) &&
929 (tmpLineLayout.length > targetWidth))
931 // Current word does not fit in the box's width.
932 if(((oneHyphenLaidOut && isHyphenMode) ||
933 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
936 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
937 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
940 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
942 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
944 // The word doesn't fit in the control's width. It needs to be split by character.
945 if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
949 tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
950 tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
954 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
955 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
958 tmpLineLayout.penX = previousTmpPenX;
959 tmpLineLayout.previousAdvance = previousTmpAdvance;
960 tmpLineLayout.length = previousTmpLength;
961 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
964 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
966 // Add part of the word to the line layout and shift the first glyph.
967 MergeLineLayout(lineLayout, tmpLineLayout, true);
969 else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
970 (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
972 // Add part of the word to the line layout.
973 MergeLineLayout(lineLayout, tmpLineLayout, false);
978 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
981 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
983 // Reorder the RTL line.
984 if(bidiParameters.isBidirectional)
986 ReorderBiDiLayout(parameters,
991 enforceEllipsisInSingleLine);
997 if((isMultiline || isLastGlyph) &&
998 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
1000 LineLayout currentLineLayout = lineLayout;
1001 oneHyphenLaidOut = false;
1003 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1005 // Must break the line. Update the line layout, shift the first glyph and return.
1006 MergeLineLayout(lineLayout, tmpLineLayout, true);
1010 // Must break the line. Update the line layout and return.
1011 MergeLineLayout(lineLayout, tmpLineLayout, false);
1014 // Reorder the RTL line.
1015 if(bidiParameters.isBidirectional)
1017 ReorderBiDiLayout(parameters,
1022 enforceEllipsisInSingleLine);
1025 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
1026 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1032 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1034 oneHyphenLaidOut = false;
1035 oneWordLaidOut = isWordLaidOut;
1036 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
1038 // Current glyph is the last one of the current word.
1039 // Add the temporal layout to the current one.
1040 MergeLineLayout(lineLayout, tmpLineLayout, false);
1042 tmpLineLayout.Clear();
1046 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1047 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1049 hyphenGlyph = GlyphInfo();
1050 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1052 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1053 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1055 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1057 if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1059 hyphenIndex = glyphIndex;
1060 oneHyphenLaidOut = true;
1062 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
1064 // Current glyph is the last one of the current word hyphen.
1065 // Add the temporal layout to the current one.
1066 MergeLineLayout(lineLayout, tmpLineLayout, false);
1068 tmpLineLayout.Clear();
1072 glyphIndex += numberOfGLyphsInGroup;
1075 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1078 void SetGlyphPositions(const Parameters& layoutParameters,
1079 Vector2* glyphPositionsBuffer,
1080 const LineLayout& layout)
1082 // Traverse the glyphs and set the positions.
1084 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1085 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1086 const Length numberOfGlyphs = layout.numberOfGlyphs;
1087 const float interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1089 const GlyphIndex startIndexForGlyph = layout.glyphIndex;
1090 const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1092 // Check if the x bearing of the first character is negative.
1093 // If it has a negative x bearing, it will exceed the boundaries of the actor,
1094 // so the penX position needs to be moved to the right.
1095 const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1096 float penX = -glyph.xBearing + mCursorWidth + outlineWidth; //
1098 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1099 layoutParameters.textModel->mLogicalModel,
1100 interGlyphExtraAdvance,
1102 startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1103 startIndexForGlyphPositions,
1104 glyphPositionsBuffer,
1107 if(layout.isSplitToTwoHalves)
1109 const GlyphIndex startIndexForGlyphInSecondHalf = layout.glyphIndexInSecondHalfLine;
1110 const Length numberOfGlyphsInSecondHalfLine = layout.numberOfGlyphsInSecondHalfLine;
1111 const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1113 CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1114 layoutParameters.textModel->mLogicalModel,
1115 interGlyphExtraAdvance,
1116 numberOfGlyphsInSecondHalfLine,
1117 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1118 startIndexForGlyphPositionsnSecondHalf,
1119 glyphPositionsBuffer,
1124 void SetGlyphPositions(const Parameters& layoutParameters,
1125 Vector2* glyphPositionsBuffer,
1126 LayoutBidiParameters& layoutBidiParameters,
1127 const LineLayout& layout)
1129 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1130 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1131 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1133 CharacterIndex characterLogicalIndex = 0u;
1134 CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1135 bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
1139 if(layout.isSplitToTwoHalves)
1141 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1142 layoutParameters.textModel->mLogicalModel,
1143 layoutBidiParameters.bidiLineIndex,
1144 layoutParameters.startGlyphIndex,
1145 glyphPositionsBuffer,
1146 characterVisualIndex,
1147 characterLogicalIndex,
1151 if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1153 extendedToSecondHalf = true;
1154 characterLogicalIndex = 0u;
1155 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1157 CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1158 layoutParameters.textModel->mLogicalModel,
1159 layoutBidiParameters.bidiLineIndex,
1160 layoutParameters.startGlyphIndex,
1161 glyphPositionsBuffer,
1162 characterVisualIndex,
1163 characterLogicalIndex,
1167 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1168 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
1170 penX += -glyph.xBearing;
1172 // Traverses the characters of the right to left paragraph.
1173 if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1175 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1176 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1177 layoutParameters.startGlyphIndex,
1178 layoutParameters.interGlyphExtraAdvance,
1179 bidiLine.characterRunForSecondHalfLine,
1180 bidiLine.visualToLogicalMapSecondHalf,
1181 glyphPositionsBuffer,
1182 characterLogicalIndex,
1186 characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1188 TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1189 layoutParameters.textModel->mLogicalModel->mText.Begin(),
1190 layoutParameters.startGlyphIndex,
1191 layoutParameters.interGlyphExtraAdvance,
1192 bidiLine.characterRun,
1193 bidiLine.visualToLogicalMap,
1194 glyphPositionsBuffer,
1195 characterLogicalIndex,
1200 * @brief Resizes the line buffer.
1202 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1203 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1204 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1205 * @param[in] updateCurrentBuffer Whether the layout is updated.
1207 * @return Pointer to either lines or newLines.
1209 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1210 Vector<LineRun>& newLines,
1211 Length& linesCapacity,
1212 bool updateCurrentBuffer)
1214 LineRun* linesBuffer = nullptr;
1215 // Reserve more space for the next lines.
1216 linesCapacity *= 2u;
1217 if(updateCurrentBuffer)
1219 newLines.Resize(linesCapacity);
1220 linesBuffer = newLines.Begin();
1224 lines.Resize(linesCapacity);
1225 linesBuffer = lines.Begin();
1232 * Ellipsis a line if it exceeds the width's of the bounding box.
1234 * @param[in] layoutParameters The parameters needed to layout the text.
1235 * @param[in] layout The line layout.
1236 * @param[in,out] layoutSize The text's layout size.
1237 * @param[in,out] linesBuffer Pointer to the line's buffer.
1238 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1239 * @param[in,out] numberOfLines The number of laid-out lines.
1240 * @param[in] penY The vertical layout position.
1241 * @param[in] currentParagraphDirection The current paragraph's direction.
1242 * @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
1243 * @param[in] ellipsisPosition Where is the location the text elide
1245 * return Whether the line is ellipsized.
1247 bool EllipsisLine(const Parameters& layoutParameters,
1248 LayoutBidiParameters& layoutBidiParameters,
1249 const LineLayout& layout,
1251 LineRun* linesBuffer,
1252 Vector2* glyphPositionsBuffer,
1253 Length& numberOfLines,
1255 bool& isAutoScrollEnabled,
1256 DevelText::EllipsisPosition::Type ellipsisPosition,
1257 bool enforceEllipsisInSingleLine)
1259 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))));
1260 const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1261 if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1263 isAutoScrollEnabled = false;
1264 // Do not layout more lines if ellipsis is enabled.
1266 // The last line needs to be completely filled with characters.
1267 // Part of a word may be used.
1269 LineRun* lineRun = nullptr;
1270 LineLayout ellipsisLayout;
1271 if(0u != numberOfLines)
1273 // Get the last line and layout it again with the 'completelyFill' flag to true.
1274 lineRun = linesBuffer + (numberOfLines - 1u);
1275 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1277 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1281 // At least there is space reserved for one line.
1282 lineRun = linesBuffer;
1284 lineRun->glyphRun.glyphIndex = 0u;
1285 ellipsisLayout.glyphIndex = 0u;
1286 lineRun->isSplitToTwoHalves = false;
1291 GetLineLayoutForBox(layoutParameters,
1292 layoutBidiParameters,
1296 enforceEllipsisInSingleLine,
1299 if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1301 lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1304 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
1305 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
1306 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1307 lineRun->width = ellipsisLayout.length;
1308 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1309 lineRun->ascender = ellipsisLayout.ascender;
1310 lineRun->descender = ellipsisLayout.descender;
1311 lineRun->ellipsis = true;
1313 lineRun->isSplitToTwoHalves = ellipsisLayout.isSplitToTwoHalves;
1314 lineRun->glyphRunSecondHalf.glyphIndex = ellipsisLayout.glyphIndexInSecondHalfLine;
1315 lineRun->glyphRunSecondHalf.numberOfGlyphs = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1316 lineRun->characterRunForSecondHalfLine.characterIndex = ellipsisLayout.characterIndexInSecondHalfLine;
1317 lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1319 layoutSize.width = layoutParameters.boundingBox.width;
1320 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1322 layoutSize.height += GetLineHeight(*lineRun, true);
1326 //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1327 //usually we don't includ it in normal cases using GetLineHeight()
1328 if(lineRun->lineSpacing < 0)
1330 layoutSize.height -= lineRun->lineSpacing;
1334 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1336 if(layoutBidiParameters.isBidirectional)
1338 layoutBidiParameters.bidiLineIndex = 0u;
1339 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1340 endIt = bidirectionalLinesInfo.End();
1342 ++it, ++layoutBidiParameters.bidiLineIndex)
1344 const BidirectionalLineInfoRun& run = *it;
1345 //To handle case when the laid characters exist in next line.
1346 //More than one BidirectionalLineInfoRun could start with same character.
1347 //When need to check also numberOfCharacters in line.
1348 //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1349 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1350 ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1351 ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1352 ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1354 // Found where to insert the bidi line info.
1360 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1362 if((nullptr != bidirectionalLineInfo) &&
1363 !bidirectionalLineInfo->isIdentity &&
1364 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1366 lineRun->direction = RTL;
1367 SetGlyphPositions(layoutParameters,
1368 glyphPositionsBuffer,
1369 layoutBidiParameters,
1374 lineRun->direction = LTR;
1375 SetGlyphPositions(layoutParameters,
1376 glyphPositionsBuffer,
1385 * @brief Updates the text layout with a new laid-out line.
1387 * @param[in] layoutParameters The parameters needed to layout the text.
1388 * @param[in] layout The line layout.
1389 * @param[in,out] layoutSize The text's layout size.
1390 * @param[in,out] linesBuffer Pointer to the line's buffer.
1391 * @param[in] index Index to the vector of glyphs.
1392 * @param[in,out] numberOfLines The number of laid-out lines.
1393 * @param[in] isLastLine Whether the laid-out line is the last one.
1395 void UpdateTextLayout(const Parameters& layoutParameters,
1396 const LineLayout& layout,
1398 LineRun* linesBuffer,
1400 Length& numberOfLines,
1403 LineRun& lineRun = *(linesBuffer + numberOfLines);
1406 lineRun.glyphRun.glyphIndex = index;
1407 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
1408 lineRun.characterRun.characterIndex = layout.characterIndex;
1409 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1410 lineRun.width = layout.length;
1411 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
1413 lineRun.isSplitToTwoHalves = layout.isSplitToTwoHalves;
1414 lineRun.glyphRunSecondHalf.glyphIndex = layout.glyphIndexInSecondHalfLine;
1415 lineRun.glyphRunSecondHalf.numberOfGlyphs = layout.numberOfGlyphsInSecondHalfLine;
1416 lineRun.characterRunForSecondHalfLine.characterIndex = layout.characterIndexInSecondHalfLine;
1417 lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1419 // Rounds upward to avoid a non integer size.
1420 lineRun.width = std::ceil(lineRun.width);
1422 lineRun.ascender = layout.ascender;
1423 lineRun.descender = layout.descender;
1424 lineRun.direction = layout.direction;
1425 lineRun.ellipsis = false;
1427 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1429 // Update the actual size.
1430 if(lineRun.width > layoutSize.width)
1432 layoutSize.width = lineRun.width;
1435 layoutSize.height += GetLineHeight(lineRun, isLastLine);
1439 * @brief Updates the text layout with the last laid-out line.
1441 * @param[in] layoutParameters The parameters needed to layout the text.
1442 * @param[in] characterIndex The character index of the line.
1443 * @param[in] glyphIndex The glyph index of the line.
1444 * @param[in,out] layoutSize The text's layout size.
1445 * @param[in,out] linesBuffer Pointer to the line's buffer.
1446 * @param[in,out] numberOfLines The number of laid-out lines.
1448 void UpdateTextLayout(const Parameters& layoutParameters,
1449 CharacterIndex characterIndex,
1450 GlyphIndex glyphIndex,
1452 LineRun* linesBuffer,
1453 Length& numberOfLines)
1455 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1457 // Need to add a new line with no characters but with height to increase the layoutSize.height
1458 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1460 Text::FontMetrics fontMetrics;
1461 if(0u != glyphInfo.fontId)
1463 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1466 LineRun& lineRun = *(linesBuffer + numberOfLines);
1469 lineRun.glyphRun.glyphIndex = glyphIndex;
1470 lineRun.glyphRun.numberOfGlyphs = 0u;
1471 lineRun.characterRun.characterIndex = characterIndex;
1472 lineRun.characterRun.numberOfCharacters = 0u;
1473 lineRun.width = 0.f;
1474 lineRun.ascender = fontMetrics.ascender;
1475 lineRun.descender = fontMetrics.descender;
1476 lineRun.extraLength = 0.f;
1477 lineRun.alignmentOffset = 0.f;
1478 lineRun.direction = LTR;
1479 lineRun.ellipsis = false;
1481 lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1483 layoutSize.height += GetLineHeight(lineRun, true);
1487 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1489 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1490 * @param[in,out] layoutSize The text's layout size.
1492 void UpdateLayoutSize(const Vector<LineRun>& lines,
1495 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1496 endIt = lines.End();
1500 const LineRun& line = *it;
1501 bool isLastLine = (it + 1 == endIt);
1503 if(line.width > layoutSize.width)
1505 layoutSize.width = line.width;
1508 layoutSize.height += GetLineHeight(line, isLastLine);
1513 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1515 * @param[in] layoutParameters The parameters needed to layout the text.
1516 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1517 * @param[in] characterOffset The offset to be added to the runs of characters.
1518 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1520 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1521 Vector<LineRun>& lines,
1522 Length characterOffset,
1525 // Update the glyph and character runs.
1526 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1527 endIt = lines.End();
1531 LineRun& line = *it;
1533 line.glyphRun.glyphIndex = glyphOffset;
1534 line.characterRun.characterIndex = characterOffset;
1536 glyphOffset += line.glyphRun.numberOfGlyphs;
1537 characterOffset += line.characterRun.numberOfCharacters;
1541 bool LayoutText(Parameters& layoutParameters,
1543 bool elideTextEnabled,
1544 bool& isAutoScrollEnabled,
1545 DevelText::EllipsisPosition::Type ellipsisPosition)
1547 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1548 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1550 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1551 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1553 //Reset indices of ElidedGlyphs
1554 layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1555 layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1556 layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1557 layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1559 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1561 if(0u == layoutParameters.numberOfGlyphs)
1563 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1564 if(layoutParameters.isLastNewParagraph)
1566 Length numberOfLines = lines.Count();
1567 if(0u != numberOfLines)
1569 const LineRun& lastLine = *(lines.End() - 1u);
1571 if(0u != lastLine.characterRun.numberOfCharacters)
1573 // Need to add a new line with no characters but with height to increase the layoutSize.height
1575 Initialize(newLine);
1576 lines.PushBack(newLine);
1578 UpdateTextLayout(layoutParameters,
1579 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1580 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1588 // Calculates the layout size.
1589 UpdateLayoutSize(lines,
1592 // Rounds upward to avoid a non integer size.
1593 layoutSize.height = std::ceil(layoutSize.height);
1595 // Nothing else do if there are no glyphs to layout.
1599 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1600 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1601 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1603 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1604 // This extra line needs to be removed.
1605 if(0u != lines.Count())
1607 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1609 if((0u == lastLine->characterRun.numberOfCharacters) &&
1610 (lastGlyphPlusOne == totalNumberOfGlyphs))
1612 lines.Remove(lastLine);
1616 // Retrieve BiDi info.
1617 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1619 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1620 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1621 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1623 // Set the layout bidirectional paramters.
1624 LayoutBidiParameters layoutBidiParameters;
1626 // Whether the layout is being updated or set from scratch.
1627 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1629 Vector2* glyphPositionsBuffer = nullptr;
1630 Vector<Vector2> newGlyphPositions;
1632 LineRun* linesBuffer = nullptr;
1633 Vector<LineRun> newLines;
1635 // Estimate the number of lines.
1636 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1637 Length numberOfLines = 0u;
1639 if(updateCurrentBuffer)
1641 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1642 glyphPositionsBuffer = newGlyphPositions.Begin();
1644 newLines.Resize(linesCapacity);
1645 linesBuffer = newLines.Begin();
1649 glyphPositionsBuffer = glyphPositions.Begin();
1651 lines.Resize(linesCapacity);
1652 linesBuffer = lines.Begin();
1655 float penY = CalculateLineOffset(lines,
1656 layoutParameters.startLineIndex);
1657 bool anyLineIsEliped = false;
1658 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1660 layoutBidiParameters.Clear();
1662 if(hasBidiParagraphs)
1664 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1666 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1667 endIt = bidirectionalParagraphsInfo.End();
1669 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1671 const BidirectionalParagraphInfoRun& run = *it;
1673 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1675 if(lastCharacterIndex <= startCharacterIndex)
1677 // Do not process, the paragraph has already been processed.
1681 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1683 layoutBidiParameters.paragraphDirection = run.direction;
1684 layoutBidiParameters.isBidirectional = true;
1687 // Has already been found.
1691 if(layoutBidiParameters.isBidirectional)
1693 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1694 endIt = bidirectionalLinesInfo.End();
1696 ++it, ++layoutBidiParameters.bidiLineIndex)
1698 const BidirectionalLineInfoRun& run = *it;
1700 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1702 if(lastCharacterIndex <= startCharacterIndex)
1708 if(startCharacterIndex < lastCharacterIndex)
1710 // Found where to insert the bidi line info.
1717 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1719 // Get the layout for the line.
1721 layout.direction = layoutBidiParameters.paragraphDirection;
1722 layout.glyphIndex = index;
1723 GetLineLayoutForBox(layoutParameters,
1724 layoutBidiParameters,
1731 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1732 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1733 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1734 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1735 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1737 if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1739 // The width is too small and no characters are laid-out.
1740 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1742 lines.Resize(numberOfLines);
1744 // Rounds upward to avoid a non integer size.
1745 layoutSize.height = std::ceil(layoutSize.height);
1750 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1752 penY += layout.ascender;
1754 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1756 bool ellipsis = false;
1757 if(elideTextEnabled)
1759 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1761 // Does the ellipsis of the last line.
1762 ellipsis = EllipsisLine(layoutParameters,
1763 layoutBidiParameters,
1767 glyphPositionsBuffer,
1770 isAutoScrollEnabled,
1775 if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1777 const bool isMultiline = mLayout == MULTI_LINE_BOX;
1778 if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1780 ellipsis = EllipsisLine(layoutParameters,
1781 layoutBidiParameters,
1785 glyphPositionsBuffer,
1788 isAutoScrollEnabled,
1793 //clear hyphen from ellipsis line
1794 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1795 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1797 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1799 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1800 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1804 // No more lines to layout.
1809 //In START location of ellipsis whether to shift lines or not.
1810 anyLineIsEliped |= ellipsis;
1812 // Whether the last line has been laid-out.
1813 const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1815 if(numberOfLines == linesCapacity)
1817 // Reserve more space for the next lines.
1818 linesBuffer = ResizeLinesBuffer(lines,
1821 updateCurrentBuffer);
1824 // Updates the current text's layout with the line's layout.
1825 UpdateTextLayout(layoutParameters,
1833 const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1835 if((nextIndex == totalNumberOfGlyphs) &&
1836 layoutParameters.isLastNewParagraph &&
1837 (mLayout == MULTI_LINE_BOX))
1839 // The last character of the text is a new paragraph character.
1840 // An extra line with no characters is added to increase the text's height
1841 // in order to place the cursor.
1843 if(numberOfLines == linesCapacity)
1845 // Reserve more space for the next lines.
1846 linesBuffer = ResizeLinesBuffer(lines,
1849 updateCurrentBuffer);
1852 UpdateTextLayout(layoutParameters,
1853 layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1854 index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1858 } // whether to add a last line.
1860 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1862 if((nullptr != bidirectionalLineInfo) &&
1863 !bidirectionalLineInfo->isIdentity &&
1864 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1866 SetGlyphPositions(layoutParameters,
1867 glyphPositionsBuffer,
1868 layoutBidiParameters,
1873 // Sets the positions of the glyphs.
1874 SetGlyphPositions(layoutParameters,
1875 glyphPositionsBuffer,
1879 // Updates the vertical pen's position.
1880 penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
1882 // Increase the glyph index.
1885 } // end for() traversing glyphs.
1887 //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1888 if(anyLineIsEliped && numberOfLines > 1u)
1890 if(ellipsisPosition == DevelText::EllipsisPosition::START)
1892 Length lineIndex = 0;
1893 while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1895 LineRun& delLine = linesBuffer[lineIndex];
1896 delLine.ellipsis = true;
1898 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1899 for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1901 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1902 linesBuffer[lineIndex].ellipsis = false;
1906 linesBuffer[0u].ellipsis = true;
1908 else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1910 Length middleLineIndex = (numberOfLines) / 2u;
1911 Length ellipsisLineIndex = 0u;
1912 while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1914 LineRun& delLine = linesBuffer[middleLineIndex];
1915 delLine.ellipsis = true;
1917 layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1918 for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1920 linesBuffer[lineIndex] = linesBuffer[lineIndex + 1];
1921 linesBuffer[lineIndex].ellipsis = false;
1924 ellipsisLineIndex = middleLineIndex - 1u;
1925 middleLineIndex = (numberOfLines) / 2u;
1928 linesBuffer[ellipsisLineIndex].ellipsis = true;
1932 if(updateCurrentBuffer)
1934 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1935 newGlyphPositions.Begin(),
1936 newGlyphPositions.End());
1937 glyphPositions.Resize(totalNumberOfGlyphs);
1939 newLines.Resize(numberOfLines);
1941 // Current text's layout size adds only the newly laid-out lines.
1942 // Updates the layout size with the previously laid-out lines.
1943 UpdateLayoutSize(lines,
1946 if(0u != newLines.Count())
1948 const LineRun& lastLine = *(newLines.End() - 1u);
1950 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1951 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1953 // Update the indices of the runs before the new laid-out lines are inserted.
1954 UpdateLineIndexOffsets(layoutParameters,
1959 // Insert the lines.
1960 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1967 lines.Resize(numberOfLines);
1970 // Rounds upward to avoid a non integer size.
1971 layoutSize.height = std::ceil(layoutSize.height);
1973 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1978 void Align(const Size& size,
1979 CharacterIndex startIndex,
1980 Length numberOfCharacters,
1981 Text::HorizontalAlignment::Type horizontalAlignment,
1982 Vector<LineRun>& lines,
1983 float& alignmentOffset,
1984 Dali::LayoutDirection::Type layoutDirection,
1985 bool matchLayoutDirection)
1987 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1989 alignmentOffset = MAX_FLOAT;
1990 // Traverse all lines and align the glyphs.
1991 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1995 LineRun& line = *it;
1997 if(line.characterRun.characterIndex < startIndex)
1999 // Do not align lines which have already been aligned.
2003 if(line.characterRun.characterIndex > lastCharacterPlusOne)
2005 // Do not align lines beyond the last laid-out character.
2009 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2011 // Do not align lines beyond the last laid-out character unless the line is last and empty.
2015 // Calculate the line's alignment offset accordingly with the align option,
2016 // the box width, line length, and the paragraph's direction.
2017 CalculateHorizontalAlignment(size.width,
2018 horizontalAlignment,
2021 matchLayoutDirection);
2023 // Updates the alignment offset.
2024 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2028 void CalculateHorizontalAlignment(float boxWidth,
2029 HorizontalAlignment::Type horizontalAlignment,
2031 Dali::LayoutDirection::Type layoutDirection,
2032 bool matchLayoutDirection)
2034 line.alignmentOffset = 0.f;
2035 const bool isLineRTL = RTL == line.direction;
2037 // Whether to swap the alignment.
2038 // 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.
2039 bool isLayoutRTL = isLineRTL;
2040 float lineLength = line.width;
2042 // match align for system language direction
2043 if(matchLayoutDirection)
2045 // Swap the alignment type if the line is right to left.
2046 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2048 // Calculate the horizontal line offset.
2049 switch(horizontalAlignment)
2051 case HorizontalAlignment::BEGIN:
2057 lineLength += line.extraLength;
2060 line.alignmentOffset = boxWidth - lineLength;
2064 line.alignmentOffset = 0.f;
2068 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2069 line.alignmentOffset -= line.extraLength;
2074 case HorizontalAlignment::CENTER:
2076 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2080 line.alignmentOffset -= line.extraLength;
2083 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2086 case HorizontalAlignment::END:
2090 line.alignmentOffset = 0.f;
2094 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2095 line.alignmentOffset -= line.extraLength;
2102 lineLength += line.extraLength;
2105 line.alignmentOffset = boxWidth - lineLength;
2112 void Initialize(LineRun& line)
2114 line.glyphRun.glyphIndex = 0u;
2115 line.glyphRun.numberOfGlyphs = 0u;
2116 line.characterRun.characterIndex = 0u;
2117 line.characterRun.numberOfCharacters = 0u;
2119 line.ascender = 0.f;
2120 line.descender = 0.f;
2121 line.extraLength = 0.f;
2122 line.alignmentOffset = 0.f;
2123 line.direction = LTR;
2124 line.ellipsis = false;
2125 line.lineSpacing = mDefaultLineSpacing;
2126 line.isSplitToTwoHalves = false;
2127 line.glyphRunSecondHalf.glyphIndex = 0u;
2128 line.glyphRunSecondHalf.numberOfGlyphs = 0u;
2129 line.characterRunForSecondHalfLine.characterIndex = 0u;
2130 line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2135 float mDefaultLineSpacing;
2136 float mDefaultLineSize;
2138 IntrusivePtr<Metrics> mMetrics;
2139 float mRelativeLineSize;
2145 mImpl = new Engine::Impl();
2153 void Engine::SetMetrics(MetricsPtr& metrics)
2155 mImpl->mMetrics = metrics;
2158 void Engine::SetLayout(Type layout)
2160 mImpl->mLayout = layout;
2163 Engine::Type Engine::GetLayout() const
2165 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2166 return mImpl->mLayout;
2169 void Engine::SetCursorWidth(int width)
2171 mImpl->mCursorWidth = static_cast<float>(width);
2174 int Engine::GetCursorWidth() const
2176 return static_cast<int>(mImpl->mCursorWidth);
2179 bool Engine::LayoutText(Parameters& layoutParameters,
2181 bool elideTextEnabled,
2182 bool& isAutoScrollEnabled,
2183 DevelText::EllipsisPosition::Type ellipsisPosition)
2185 return mImpl->LayoutText(layoutParameters,
2188 isAutoScrollEnabled,
2192 void Engine::Align(const Size& size,
2193 CharacterIndex startIndex,
2194 Length numberOfCharacters,
2195 Text::HorizontalAlignment::Type horizontalAlignment,
2196 Vector<LineRun>& lines,
2197 float& alignmentOffset,
2198 Dali::LayoutDirection::Type layoutDirection,
2199 bool matchLayoutDirection)
2204 horizontalAlignment,
2208 matchLayoutDirection);
2211 void Engine::SetDefaultLineSpacing(float lineSpacing)
2213 mImpl->mDefaultLineSpacing = lineSpacing;
2216 float Engine::GetDefaultLineSpacing() const
2218 return mImpl->mDefaultLineSpacing;
2221 void Engine::SetDefaultLineSize(float lineSize)
2223 mImpl->mDefaultLineSize = lineSize;
2226 float Engine::GetDefaultLineSize() const
2228 return mImpl->mDefaultLineSize;
2231 void Engine::SetRelativeLineSize(float relativeLineSize)
2233 mImpl->mRelativeLineSize = relativeLineSize;
2236 float Engine::GetRelativeLineSize() const
2238 return mImpl->mRelativeLineSize;
2241 } // namespace Layout
2245 } // namespace Toolkit