2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
43 #if defined(DEBUG_ENABLED)
44 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
47 const float MAX_FLOAT = std::numeric_limits<float>::max();
48 const CharacterDirection LTR = false;
49 const CharacterDirection RTL = !LTR;
50 const float LINE_SPACING = 0.f;
51 const float MIN_LINE_SIZE = 0.f;
52 const Character HYPHEN_UNICODE = 0x002D;
54 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
56 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
62 * @brief Stores temporary layout info of the line.
70 numberOfCharacters{0u},
77 whiteSpaceLengthEndOfLine{0.f},
91 numberOfCharacters = 0u;
92 ascender = -MAX_FLOAT;
93 descender = MAX_FLOAT;
97 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
98 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
99 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
100 Length numberOfCharacters; ///< The number of characters which fit in one line.
101 float ascender; ///< The maximum ascender of all fonts in the line.
102 float descender; ///< The minimum descender of all fonts in the line.
103 float lineSpacing; ///< The line spacing
104 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
105 float previousAdvance; ///< The advance of the previous glyph.
106 float length; ///< The current length of the line.
107 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
108 CharacterDirection direction;
111 struct LayoutBidiParameters
115 paragraphDirection = LTR;
116 bidiParagraphIndex = 0u;
118 isBidirectional = false;
121 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
122 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
123 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
124 bool isBidirectional = false; ///< Whether the text is bidirectional.
130 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
132 mDefaultLineSpacing{LINE_SPACING},
133 mDefaultLineSize{MIN_LINE_SIZE}
138 * @brief Updates the line ascender and descender with the metrics of a new font.
140 * @param[in] glyphMetrics The metrics of the new font.
141 * @param[in,out] lineLayout The line layout.
143 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
145 Text::FontMetrics fontMetrics;
146 if(0u != glyphMetrics.fontId)
148 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
152 fontMetrics.ascender = glyphMetrics.fontHeight;
153 fontMetrics.descender = 0.f;
154 fontMetrics.height = fontMetrics.ascender;
155 fontMetrics.underlinePosition = 0.f;
156 fontMetrics.underlineThickness = 1.f;
159 // Sets the maximum ascender.
160 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
162 // Sets the minimum descender.
163 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
165 // Sets the line size
166 lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
167 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
169 // Add the line spacing
170 lineLayout.lineSpacing += mDefaultLineSpacing;
174 * @brief Merges a temporary line layout into the line layout.
176 * @param[in,out] lineLayout The line layout.
177 * @param[in] tmpLineLayout A temporary line layout.
179 void MergeLineLayout(LineLayout& lineLayout,
180 const LineLayout& tmpLineLayout)
182 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
183 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
185 lineLayout.penX = tmpLineLayout.penX;
186 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
188 lineLayout.length = tmpLineLayout.length;
189 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
191 // Sets the maximum ascender.
192 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
194 // Sets the minimum descender.
195 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
198 void LayoutRightToLeft(const Parameters& parameters,
199 const BidirectionalLineInfoRun& bidirectionalLineInfo,
201 float& whiteSpaceLengthEndOfLine)
203 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
204 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
205 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
206 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
208 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
209 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
211 CharacterIndex characterLogicalIndex = 0u;
212 CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
214 if(RTL == bidirectionalLineInfo.direction)
216 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
218 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
220 whiteSpaceLengthEndOfLine += glyphInfo.advance;
222 ++characterLogicalIndex;
223 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
227 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
229 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
230 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
231 lastGlyphOfParagraphPlusOne,
232 charactersPerGlyphBuffer);
234 GlyphMetrics glyphMetrics;
235 GetGlyphsMetrics(glyphIndex,
236 numberOfGLyphsInGroup,
241 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
243 // Traverses the characters of the right to left paragraph.
244 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
246 // Convert the character in the logical order into the character in the visual order.
247 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
248 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
250 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
252 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
253 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
254 lastGlyphOfParagraphPlusOne,
255 charactersPerGlyphBuffer);
257 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
259 GlyphMetrics glyphMetrics;
260 GetGlyphsMetrics(glyphIndex,
261 numberOfGLyphsInGroup,
268 if(RTL == bidirectionalLineInfo.direction)
270 length += glyphMetrics.advance;
274 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
276 penX += glyphMetrics.advance;
280 if(LTR == bidirectionalLineInfo.direction)
282 whiteSpaceLengthEndOfLine = 0.f;
284 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
285 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
290 void ReorderBiDiLayout(const Parameters& parameters,
291 LayoutBidiParameters& bidiParameters,
292 const LineLayout& currentLineLayout,
293 LineLayout& lineLayout,
294 bool breakInCharacters)
296 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
298 // The last glyph to be laid-out.
299 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
301 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
303 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
304 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
305 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
307 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
309 // Sets the visual to logical map tables needed to reorder the text.
310 ReorderLine(bidirectionalParagraphInfo,
311 bidirectionalLinesInfo,
312 bidiParameters.bidiLineIndex,
313 lineLayout.characterIndex,
314 lineLayout.numberOfCharacters,
315 bidiParameters.paragraphDirection);
317 // Recalculate the length of the line and update the layout.
318 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
320 if(!bidirectionalLineInfo.isIdentity)
323 float whiteSpaceLengthEndOfLine = 0.f;
324 LayoutRightToLeft(parameters,
325 bidirectionalLineInfo,
327 whiteSpaceLengthEndOfLine);
329 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
330 if(!Equals(length, lineLayout.length))
332 const bool isMultiline = mLayout == MULTI_LINE_BOX;
334 if(isMultiline && (length > parameters.boundingBox.width))
336 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
338 // The word doesn't fit in one line. It has to be split by character.
340 // Remove the last laid out glyph(s) as they doesn't fit.
341 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
343 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
344 lastGlyphOfParagraphPlusOne,
345 charactersPerGlyphBuffer);
347 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
349 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
350 lineLayout.numberOfCharacters -= numberOfCharacters;
352 AdjustLayout(parameters,
354 bidirectionalParagraphInfo,
357 if(lineLayout.length < parameters.boundingBox.width)
362 if(glyphIndex < numberOfGLyphsInGroup)
364 // avoids go under zero for an unsigned int.
368 glyphIndex -= numberOfGLyphsInGroup;
373 lineLayout = currentLineLayout;
375 AdjustLayout(parameters,
377 bidirectionalParagraphInfo,
383 lineLayout.length = std::max(length, lineLayout.length);
390 void AdjustLayout(const Parameters& parameters,
391 LayoutBidiParameters& bidiParameters,
392 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
393 LineLayout& lineLayout)
395 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
397 // Remove current reordered line.
398 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
400 // Re-build the conversion table without the removed glyphs.
401 ReorderLine(bidirectionalParagraphInfo,
402 bidirectionalLinesInfo,
403 bidiParameters.bidiLineIndex,
404 lineLayout.characterIndex,
405 lineLayout.numberOfCharacters,
406 bidiParameters.paragraphDirection);
408 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
411 float whiteSpaceLengthEndOfLine = 0.f;
412 LayoutRightToLeft(parameters,
413 bidirectionalLineInfo,
415 whiteSpaceLengthEndOfLine);
417 lineLayout.length = length;
418 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
422 * Retrieves the line layout for a given box width.
424 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
425 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
426 * to reorder the line and recalculate its length.
429 * @param[in] parameters The layout parameters.
430 * @param[] bidiParameters Bidirectional info for the current line.
431 * @param[out] lineLayout The line layout.
432 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
434 void GetLineLayoutForBox(const Parameters& parameters,
435 LayoutBidiParameters& bidiParameters,
436 LineLayout& lineLayout,
439 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
440 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
442 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
443 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
444 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
445 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
446 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
448 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
449 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
451 const bool isMultiline = mLayout == MULTI_LINE_BOX;
452 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
453 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
454 (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
455 const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
456 const bool isMixedMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
458 // The last glyph to be laid-out.
459 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
461 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
462 // In the case the line starts with a right to left character, if the width is longer than the advance,
463 // the difference needs to be added to the line length.
465 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
466 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
467 lastGlyphOfParagraphPlusOne,
468 charactersPerGlyphBuffer);
470 GlyphMetrics glyphMetrics;
471 GetGlyphsMetrics(lineLayout.glyphIndex,
472 numberOfGLyphsInGroup,
477 // Set the direction of the first character of the line.
478 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
480 // Stores temporary line layout which has not been added to the final line layout.
481 LineLayout tmpLineLayout;
483 // Initialize the start point.
485 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
486 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
487 // 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.
488 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
490 // Calculate the line height if there is no characters.
491 FontId lastFontId = glyphMetrics.fontId;
492 UpdateLineHeight(glyphMetrics, tmpLineLayout);
494 bool oneWordLaidOut = false;
495 bool oneHyphenLaidOut = false;
496 GlyphIndex hyphenIndex = 0;
497 GlyphInfo hyphenGlyph;
499 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
500 glyphIndex < lastGlyphOfParagraphPlusOne;)
502 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
504 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
505 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
506 lastGlyphOfParagraphPlusOne,
507 charactersPerGlyphBuffer);
509 GlyphMetrics glyphMetrics;
510 GetGlyphsMetrics(glyphIndex,
511 numberOfGLyphsInGroup,
516 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
518 // Check if the font of the current glyph is the same of the previous one.
519 // If it's different the ascender and descender need to be updated.
520 if(lastFontId != glyphMetrics.fontId)
522 UpdateLineHeight(glyphMetrics, tmpLineLayout);
523 lastFontId = glyphMetrics.fontId;
526 // Get the character indices for the current glyph. The last character index is needed
527 // because there are glyphs formed by more than one character but their break info is
528 // given only for the last character.
529 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
530 const bool hasCharacters = charactersPerGlyph > 0u;
531 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
532 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
534 // Get the line break info for the current character.
535 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
537 // Increase the number of characters.
538 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
540 // Increase the number of glyphs.
541 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
543 // Check whether is a white space.
544 const Character character = *(textBuffer + characterFirstIndex);
545 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
547 // Calculate the length of the line.
549 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
550 const float previousTmpPenX = tmpLineLayout.penX;
551 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
552 const float previousTmpLength = tmpLineLayout.length;
553 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
557 // Add the length to the length of white spaces at the end of the line.
558 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
562 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
563 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
565 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
567 // Clear the white space length at the end of the line.
568 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
571 // Check if the accumulated length fits in the width of the box.
572 if((completelyFill || isMultiline) && !isWhiteSpace &&
573 (tmpLineLayout.length > parameters.boundingBox.width))
575 // Current word does not fit in the box's width.
576 if(((oneHyphenLaidOut && isHyphenMode) ||
577 (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
580 parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
581 parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
584 if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
586 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
588 // The word doesn't fit in the control's width. It needs to be split by character.
589 if(tmpLineLayout.numberOfGlyphs > 0u)
591 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
592 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
594 tmpLineLayout.penX = previousTmpPenX;
595 tmpLineLayout.previousAdvance = previousTmpAdvance;
596 tmpLineLayout.length = previousTmpLength;
597 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
600 // Add part of the word to the line layout.
601 MergeLineLayout(lineLayout, tmpLineLayout);
605 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
608 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
610 // Reorder the RTL line.
611 if(bidiParameters.isBidirectional)
613 ReorderBiDiLayout(parameters,
623 if((isMultiline || isLastGlyph) &&
624 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
626 LineLayout currentLineLayout = lineLayout;
627 oneHyphenLaidOut = false;
628 // Must break the line. Update the line layout and return.
629 MergeLineLayout(lineLayout, tmpLineLayout);
631 // Reorder the RTL line.
632 if(bidiParameters.isBidirectional)
634 ReorderBiDiLayout(parameters,
641 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
642 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
648 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
650 oneHyphenLaidOut = false;
651 oneWordLaidOut = isWordLaidOut;
652 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
654 // Current glyph is the last one of the current word.
655 // Add the temporal layout to the current one.
656 MergeLineLayout(lineLayout, tmpLineLayout);
658 tmpLineLayout.Clear();
662 ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
663 (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
665 hyphenGlyph = GlyphInfo();
666 hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
668 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
669 hyphenGlyph.index = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
671 mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
673 if((tmpLineLayout.length + hyphenGlyph.width) <= parameters.boundingBox.width)
675 hyphenIndex = glyphIndex;
676 oneHyphenLaidOut = true;
678 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One hyphen laid-out\n");
680 // Current glyph is the last one of the current word hyphen.
681 // Add the temporal layout to the current one.
682 MergeLineLayout(lineLayout, tmpLineLayout);
684 tmpLineLayout.Clear();
688 glyphIndex += numberOfGLyphsInGroup;
691 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
694 void SetGlyphPositions(const GlyphInfo* const glyphsBuffer,
695 Length numberOfGlyphs,
697 float interGlyphExtraAdvance,
698 Vector2* glyphPositionsBuffer)
700 // Traverse the glyphs and set the positions.
702 // Check if the x bearing of the first character is negative.
703 // If it has a negative x bearing, it will exceed the boundaries of the actor,
704 // so the penX position needs to be moved to the right.
706 const GlyphInfo& glyph = *glyphsBuffer;
707 float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
709 for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
711 const GlyphInfo& glyph = *(glyphsBuffer + i);
712 Vector2& position = *(glyphPositionsBuffer + i);
714 position.x = penX + glyph.xBearing;
715 position.y = -glyph.yBearing;
717 penX += (glyph.advance + interGlyphExtraAdvance);
721 void SetGlyphPositions(const Parameters& layoutParameters,
722 Vector2* glyphPositionsBuffer,
723 LayoutBidiParameters& layoutBidiParameters,
724 const LineLayout& layout)
726 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
727 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
728 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
729 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
730 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
732 CharacterIndex characterLogicalIndex = 0u;
733 CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
736 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
738 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
739 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
741 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
743 position.y = -glyph.yBearing;
745 penX += glyph.advance;
747 ++characterLogicalIndex;
748 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
751 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
752 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
754 penX += -glyph.xBearing;
756 // Traverses the characters of the right to left paragraph.
757 for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
758 ++characterLogicalIndex)
760 // Convert the character in the logical order into the character in the visual order.
761 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
763 // Get the number of glyphs of the character.
764 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
766 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
768 // Convert the character in the visual order into the glyph in the visual order.
769 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
771 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
773 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
774 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
776 position.x = penX + glyph.xBearing;
777 position.y = -glyph.yBearing;
779 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
785 * @brief Resizes the line buffer.
787 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
788 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
789 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
790 * @param[in] updateCurrentBuffer Whether the layout is updated.
792 * @return Pointer to either lines or newLines.
794 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
795 Vector<LineRun>& newLines,
796 Length& linesCapacity,
797 bool updateCurrentBuffer)
799 LineRun* linesBuffer = nullptr;
800 // Reserve more space for the next lines.
802 if(updateCurrentBuffer)
804 newLines.Resize(linesCapacity);
805 linesBuffer = newLines.Begin();
809 lines.Resize(linesCapacity);
810 linesBuffer = lines.Begin();
817 * Ellipsis a line if it exceeds the width's of the bounding box.
819 * @param[in] layoutParameters The parameters needed to layout the text.
820 * @param[in] layout The line layout.
821 * @param[in,out] layoutSize The text's layout size.
822 * @param[in,out] linesBuffer Pointer to the line's buffer.
823 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
824 * @param[in,out] numberOfLines The number of laid-out lines.
825 * @param[in] penY The vertical layout position.
826 * @param[in] currentParagraphDirection The current paragraph's direction.
827 * @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
829 * return Whether the line is ellipsized.
831 bool EllipsisLine(const Parameters& layoutParameters,
832 LayoutBidiParameters& layoutBidiParameters,
833 const LineLayout& layout,
835 LineRun* linesBuffer,
836 Vector2* glyphPositionsBuffer,
837 Length& numberOfLines,
839 bool& isAutoScrollEnabled)
841 const bool ellipsis = isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width)));
845 isAutoScrollEnabled = false;
846 // Do not layout more lines if ellipsis is enabled.
848 // The last line needs to be completely filled with characters.
849 // Part of a word may be used.
851 LineRun* lineRun = nullptr;
852 LineLayout ellipsisLayout;
853 if(0u != numberOfLines)
855 // Get the last line and layout it again with the 'completelyFill' flag to true.
856 lineRun = linesBuffer + (numberOfLines - 1u);
858 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
860 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
864 // At least there is space reserved for one line.
865 lineRun = linesBuffer;
867 lineRun->glyphRun.glyphIndex = 0u;
868 ellipsisLayout.glyphIndex = 0u;
873 GetLineLayoutForBox(layoutParameters,
874 layoutBidiParameters,
878 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
879 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
880 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
881 lineRun->width = ellipsisLayout.length;
882 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
883 lineRun->ascender = ellipsisLayout.ascender;
884 lineRun->descender = ellipsisLayout.descender;
885 lineRun->ellipsis = true;
887 layoutSize.width = layoutParameters.boundingBox.width;
888 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
890 layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
893 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
894 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
896 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
898 if(layoutBidiParameters.isBidirectional)
900 layoutBidiParameters.bidiLineIndex = 0u;
901 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
902 endIt = bidirectionalLinesInfo.End();
904 ++it, ++layoutBidiParameters.bidiLineIndex)
906 const BidirectionalLineInfoRun& run = *it;
908 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex)
910 // Found where to insert the bidi line info.
916 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
918 if((nullptr != bidirectionalLineInfo) &&
919 !bidirectionalLineInfo->isIdentity &&
920 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
922 lineRun->direction = RTL;
923 SetGlyphPositions(layoutParameters,
924 glyphPositionsBuffer,
925 layoutBidiParameters,
930 lineRun->direction = LTR;
931 SetGlyphPositions(glyphsBuffer + lineRun->glyphRun.glyphIndex,
932 ellipsisLayout.numberOfGlyphs,
934 layoutParameters.interGlyphExtraAdvance,
935 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex);
943 * @brief Updates the text layout with a new laid-out line.
945 * @param[in] layoutParameters The parameters needed to layout the text.
946 * @param[in] layout The line layout.
947 * @param[in,out] layoutSize The text's layout size.
948 * @param[in,out] linesBuffer Pointer to the line's buffer.
949 * @param[in] index Index to the vector of glyphs.
950 * @param[in,out] numberOfLines The number of laid-out lines.
951 * @param[in] isLastLine Whether the laid-out line is the last one.
953 void UpdateTextLayout(const Parameters& layoutParameters,
954 const LineLayout& layout,
956 LineRun* linesBuffer,
958 Length& numberOfLines,
961 LineRun& lineRun = *(linesBuffer + numberOfLines);
964 lineRun.glyphRun.glyphIndex = index;
965 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
966 lineRun.characterRun.characterIndex = layout.characterIndex;
967 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
968 lineRun.width = layout.length;
969 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
971 // Rounds upward to avoid a non integer size.
972 lineRun.width = std::ceil(lineRun.width);
974 lineRun.ascender = layout.ascender;
975 lineRun.descender = layout.descender;
976 lineRun.direction = layout.direction;
977 lineRun.ellipsis = false;
979 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
980 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
982 lineRun.lineSpacing += mDefaultLineSpacing;
984 // Update the actual size.
985 if(lineRun.width > layoutSize.width)
987 layoutSize.width = lineRun.width;
990 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
994 * @brief Updates the text layout with the last laid-out line.
996 * @param[in] layoutParameters The parameters needed to layout the text.
997 * @param[in] characterIndex The character index of the line.
998 * @param[in] glyphIndex The glyph index of the line.
999 * @param[in,out] layoutSize The text's layout size.
1000 * @param[in,out] linesBuffer Pointer to the line's buffer.
1001 * @param[in,out] numberOfLines The number of laid-out lines.
1003 void UpdateTextLayout(const Parameters& layoutParameters,
1004 CharacterIndex characterIndex,
1005 GlyphIndex glyphIndex,
1007 LineRun* linesBuffer,
1008 Length& numberOfLines)
1010 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1012 // Need to add a new line with no characters but with height to increase the layoutSize.height
1013 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1015 Text::FontMetrics fontMetrics;
1016 if(0u != glyphInfo.fontId)
1018 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1021 LineRun& lineRun = *(linesBuffer + numberOfLines);
1024 lineRun.glyphRun.glyphIndex = glyphIndex;
1025 lineRun.glyphRun.numberOfGlyphs = 0u;
1026 lineRun.characterRun.characterIndex = characterIndex;
1027 lineRun.characterRun.numberOfCharacters = 0u;
1028 lineRun.width = 0.f;
1029 lineRun.ascender = fontMetrics.ascender;
1030 lineRun.descender = fontMetrics.descender;
1031 lineRun.extraLength = 0.f;
1032 lineRun.alignmentOffset = 0.f;
1033 lineRun.direction = LTR;
1034 lineRun.ellipsis = false;
1036 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
1037 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1039 lineRun.lineSpacing += mDefaultLineSpacing;
1041 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
1045 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1047 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1048 * @param[in,out] layoutSize The text's layout size.
1050 void UpdateLayoutSize(const Vector<LineRun>& lines,
1053 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1054 endIt = lines.End();
1058 const LineRun& line = *it;
1060 if(line.width > layoutSize.width)
1062 layoutSize.width = line.width;
1065 layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
1070 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1072 * @param[in] layoutParameters The parameters needed to layout the text.
1073 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1074 * @param[in] characterOffset The offset to be added to the runs of characters.
1075 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1077 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1078 Vector<LineRun>& lines,
1079 Length characterOffset,
1082 // Update the glyph and character runs.
1083 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1084 endIt = lines.End();
1088 LineRun& line = *it;
1090 line.glyphRun.glyphIndex = glyphOffset;
1091 line.characterRun.characterIndex = characterOffset;
1093 glyphOffset += line.glyphRun.numberOfGlyphs;
1094 characterOffset += line.characterRun.numberOfCharacters;
1098 bool LayoutText(Parameters& layoutParameters,
1100 bool elideTextEnabled,
1101 bool& isAutoScrollEnabled)
1103 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1104 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1106 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1107 layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1109 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1111 if(0u == layoutParameters.numberOfGlyphs)
1113 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1114 if(layoutParameters.isLastNewParagraph)
1116 Length numberOfLines = lines.Count();
1117 if(0u != numberOfLines)
1119 const LineRun& lastLine = *(lines.End() - 1u);
1121 if(0u != lastLine.characterRun.numberOfCharacters)
1123 // Need to add a new line with no characters but with height to increase the layoutSize.height
1125 Initialize(newLine);
1126 lines.PushBack(newLine);
1128 UpdateTextLayout(layoutParameters,
1129 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1130 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1138 // Calculates the layout size.
1139 UpdateLayoutSize(lines,
1142 // Rounds upward to avoid a non integer size.
1143 layoutSize.height = std::ceil(layoutSize.height);
1145 // Nothing else do if there are no glyphs to layout.
1149 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1150 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1151 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1153 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1154 // This extra line needs to be removed.
1155 if(0u != lines.Count())
1157 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1159 if((0u == lastLine->characterRun.numberOfCharacters) &&
1160 (lastGlyphPlusOne == totalNumberOfGlyphs))
1162 lines.Remove(lastLine);
1166 // Retrieve BiDi info.
1167 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1169 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1170 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1171 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1173 // Set the layout bidirectional paramters.
1174 LayoutBidiParameters layoutBidiParameters;
1176 // Whether the layout is being updated or set from scratch.
1177 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1179 Vector2* glyphPositionsBuffer = nullptr;
1180 Vector<Vector2> newGlyphPositions;
1182 LineRun* linesBuffer = nullptr;
1183 Vector<LineRun> newLines;
1185 // Estimate the number of lines.
1186 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1187 Length numberOfLines = 0u;
1189 if(updateCurrentBuffer)
1191 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1192 glyphPositionsBuffer = newGlyphPositions.Begin();
1194 newLines.Resize(linesCapacity);
1195 linesBuffer = newLines.Begin();
1199 glyphPositionsBuffer = glyphPositions.Begin();
1201 lines.Resize(linesCapacity);
1202 linesBuffer = lines.Begin();
1205 float penY = CalculateLineOffset(lines,
1206 layoutParameters.startLineIndex);
1207 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1209 layoutBidiParameters.Clear();
1211 if(hasBidiParagraphs)
1213 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1215 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1216 endIt = bidirectionalParagraphsInfo.End();
1218 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1220 const BidirectionalParagraphInfoRun& run = *it;
1222 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1224 if(lastCharacterIndex <= startCharacterIndex)
1226 // Do not process, the paragraph has already been processed.
1230 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1232 layoutBidiParameters.paragraphDirection = run.direction;
1233 layoutBidiParameters.isBidirectional = true;
1236 // Has already been found.
1240 if(layoutBidiParameters.isBidirectional)
1242 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1243 endIt = bidirectionalLinesInfo.End();
1245 ++it, ++layoutBidiParameters.bidiLineIndex)
1247 const BidirectionalLineInfoRun& run = *it;
1249 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1251 if(lastCharacterIndex <= startCharacterIndex)
1257 if(startCharacterIndex < lastCharacterIndex)
1259 // Found where to insert the bidi line info.
1266 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1268 // Get the layout for the line.
1270 layout.direction = layoutBidiParameters.paragraphDirection;
1271 layout.glyphIndex = index;
1272 GetLineLayoutForBox(layoutParameters,
1273 layoutBidiParameters,
1277 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1278 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1279 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1280 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1281 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1283 if(0u == layout.numberOfGlyphs)
1285 // The width is too small and no characters are laid-out.
1286 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1288 lines.Resize(numberOfLines);
1290 // Rounds upward to avoid a non integer size.
1291 layoutSize.height = std::ceil(layoutSize.height);
1296 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1298 penY += layout.ascender;
1300 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1302 bool ellipsis = false;
1303 if(elideTextEnabled)
1305 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1307 // Does the ellipsis of the last line.
1308 ellipsis = EllipsisLine(layoutParameters,
1309 layoutBidiParameters,
1313 glyphPositionsBuffer,
1316 isAutoScrollEnabled);
1321 //clear hyphen from ellipsis line
1322 const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1323 Length hyphensCount = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1325 while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1327 layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1328 layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1332 // No more lines to layout.
1337 // Whether the last line has been laid-out.
1338 const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1340 if(numberOfLines == linesCapacity)
1342 // Reserve more space for the next lines.
1343 linesBuffer = ResizeLinesBuffer(lines,
1346 updateCurrentBuffer);
1349 // Updates the current text's layout with the line's layout.
1350 UpdateTextLayout(layoutParameters,
1358 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1360 if((nextIndex == totalNumberOfGlyphs) &&
1361 layoutParameters.isLastNewParagraph &&
1362 (mLayout == MULTI_LINE_BOX))
1364 // The last character of the text is a new paragraph character.
1365 // An extra line with no characters is added to increase the text's height
1366 // in order to place the cursor.
1368 if(numberOfLines == linesCapacity)
1370 // Reserve more space for the next lines.
1371 linesBuffer = ResizeLinesBuffer(lines,
1374 updateCurrentBuffer);
1377 UpdateTextLayout(layoutParameters,
1378 layout.characterIndex + layout.numberOfCharacters,
1379 index + layout.numberOfGlyphs,
1383 } // whether to add a last line.
1385 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1386 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1388 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1390 if((nullptr != bidirectionalLineInfo) &&
1391 !bidirectionalLineInfo->isIdentity &&
1392 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1394 SetGlyphPositions(layoutParameters,
1395 glyphPositionsBuffer,
1396 layoutBidiParameters,
1401 // Sets the positions of the glyphs.
1402 SetGlyphPositions(glyphsBuffer + index,
1403 layout.numberOfGlyphs,
1405 layoutParameters.interGlyphExtraAdvance,
1406 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex);
1409 // Updates the vertical pen's position.
1410 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1411 // If there is a defaultLineSize, updates the pen's position.
1412 if(mDefaultLineSize > 0.f)
1414 float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1415 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1416 penY += lineSpacing;
1419 // Increase the glyph index.
1422 } // end for() traversing glyphs.
1424 if(updateCurrentBuffer)
1426 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1427 newGlyphPositions.Begin(),
1428 newGlyphPositions.End());
1429 glyphPositions.Resize(totalNumberOfGlyphs);
1431 newLines.Resize(numberOfLines);
1433 // Current text's layout size adds only the newly laid-out lines.
1434 // Updates the layout size with the previously laid-out lines.
1435 UpdateLayoutSize(lines,
1438 if(0u != newLines.Count())
1440 const LineRun& lastLine = *(newLines.End() - 1u);
1442 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1443 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1445 // Update the indices of the runs before the new laid-out lines are inserted.
1446 UpdateLineIndexOffsets(layoutParameters,
1451 // Insert the lines.
1452 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1459 lines.Resize(numberOfLines);
1462 // Rounds upward to avoid a non integer size.
1463 layoutSize.height = std::ceil(layoutSize.height);
1465 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1470 void Align(const Size& size,
1471 CharacterIndex startIndex,
1472 Length numberOfCharacters,
1473 Text::HorizontalAlignment::Type horizontalAlignment,
1474 Vector<LineRun>& lines,
1475 float& alignmentOffset,
1476 Dali::LayoutDirection::Type layoutDirection,
1477 bool matchSystemLanguageDirection)
1479 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1481 alignmentOffset = MAX_FLOAT;
1482 // Traverse all lines and align the glyphs.
1483 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1487 LineRun& line = *it;
1489 if(line.characterRun.characterIndex < startIndex)
1491 // Do not align lines which have already been aligned.
1495 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1497 // Do not align lines beyond the last laid-out character.
1501 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1503 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1507 // Calculate the line's alignment offset accordingly with the align option,
1508 // the box width, line length, and the paragraph's direction.
1509 CalculateHorizontalAlignment(size.width,
1510 horizontalAlignment,
1513 matchSystemLanguageDirection);
1515 // Updates the alignment offset.
1516 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1520 void CalculateHorizontalAlignment(float boxWidth,
1521 HorizontalAlignment::Type horizontalAlignment,
1523 Dali::LayoutDirection::Type layoutDirection,
1524 bool matchSystemLanguageDirection)
1526 line.alignmentOffset = 0.f;
1527 const bool isLineRTL = RTL == line.direction;
1529 // Whether to swap the alignment.
1530 // 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.
1531 bool isLayoutRTL = isLineRTL;
1532 float lineLength = line.width;
1534 // match align for system language direction
1535 if(matchSystemLanguageDirection)
1537 // Swap the alignment type if the line is right to left.
1538 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1540 // Calculate the horizontal line offset.
1541 switch(horizontalAlignment)
1543 case HorizontalAlignment::BEGIN:
1549 lineLength += line.extraLength;
1552 line.alignmentOffset = boxWidth - lineLength;
1556 line.alignmentOffset = 0.f;
1560 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1561 line.alignmentOffset -= line.extraLength;
1566 case HorizontalAlignment::CENTER:
1568 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
1572 line.alignmentOffset -= line.extraLength;
1575 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
1578 case HorizontalAlignment::END:
1582 line.alignmentOffset = 0.f;
1586 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1587 line.alignmentOffset -= line.extraLength;
1594 lineLength += line.extraLength;
1597 line.alignmentOffset = boxWidth - lineLength;
1604 void Initialize(LineRun& line)
1606 line.glyphRun.glyphIndex = 0u;
1607 line.glyphRun.numberOfGlyphs = 0u;
1608 line.characterRun.characterIndex = 0u;
1609 line.characterRun.numberOfCharacters = 0u;
1611 line.ascender = 0.f;
1612 line.descender = 0.f;
1613 line.extraLength = 0.f;
1614 line.alignmentOffset = 0.f;
1615 line.direction = LTR;
1616 line.ellipsis = false;
1617 line.lineSpacing = mDefaultLineSpacing;
1622 float mDefaultLineSpacing;
1623 float mDefaultLineSize;
1625 IntrusivePtr<Metrics> mMetrics;
1631 mImpl = new Engine::Impl();
1639 void Engine::SetMetrics(MetricsPtr& metrics)
1641 mImpl->mMetrics = metrics;
1644 void Engine::SetLayout(Type layout)
1646 mImpl->mLayout = layout;
1649 Engine::Type Engine::GetLayout() const
1651 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1652 return mImpl->mLayout;
1655 void Engine::SetCursorWidth(int width)
1657 mImpl->mCursorWidth = static_cast<float>(width);
1660 int Engine::GetCursorWidth() const
1662 return static_cast<int>(mImpl->mCursorWidth);
1665 bool Engine::LayoutText(Parameters& layoutParameters,
1667 bool elideTextEnabled,
1668 bool& isAutoScrollEnabled)
1670 return mImpl->LayoutText(layoutParameters,
1673 isAutoScrollEnabled);
1676 void Engine::Align(const Size& size,
1677 CharacterIndex startIndex,
1678 Length numberOfCharacters,
1679 Text::HorizontalAlignment::Type horizontalAlignment,
1680 Vector<LineRun>& lines,
1681 float& alignmentOffset,
1682 Dali::LayoutDirection::Type layoutDirection,
1683 bool matchSystemLanguageDirection)
1688 horizontalAlignment,
1692 matchSystemLanguageDirection);
1695 void Engine::SetDefaultLineSpacing(float lineSpacing)
1697 mImpl->mDefaultLineSpacing = lineSpacing;
1700 float Engine::GetDefaultLineSpacing() const
1702 return mImpl->mDefaultLineSpacing;
1705 void Engine::SetDefaultLineSize(float lineSize)
1707 mImpl->mDefaultLineSize = lineSize;
1710 float Engine::GetDefaultLineSize() const
1712 return mImpl->mDefaultLineSize;
1715 } // namespace Layout
1719 } // namespace Toolkit