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;
53 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
55 return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
61 * @brief Stores temporary layout info of the line.
69 numberOfCharacters{0u},
76 whiteSpaceLengthEndOfLine{0.f},
90 numberOfCharacters = 0u;
91 ascender = -MAX_FLOAT;
92 descender = MAX_FLOAT;
96 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
97 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
98 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
99 Length numberOfCharacters; ///< The number of characters which fit in one line.
100 float ascender; ///< The maximum ascender of all fonts in the line.
101 float descender; ///< The minimum descender of all fonts in the line.
102 float lineSpacing; ///< The line spacing
103 float penX; ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
104 float previousAdvance; ///< The advance of the previous glyph.
105 float length; ///< The current length of the line.
106 float whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
107 CharacterDirection direction;
110 struct LayoutBidiParameters
114 paragraphDirection = LTR;
115 bidiParagraphIndex = 0u;
117 isBidirectional = false;
120 CharacterDirection paragraphDirection = LTR; ///< The paragraph's direction.
121 BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
122 BidirectionalLineRunIndex bidiLineIndex = 0u; ///< Index where to insert the next bidi line info.
123 bool isBidirectional = false; ///< Whether the text is bidirectional.
129 : mLayout{Layout::Engine::SINGLE_LINE_BOX},
131 mDefaultLineSpacing{LINE_SPACING},
132 mDefaultLineSize{MIN_LINE_SIZE}
137 * @brief Updates the line ascender and descender with the metrics of a new font.
139 * @param[in] glyphMetrics The metrics of the new font.
140 * @param[in,out] lineLayout The line layout.
142 void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
144 Text::FontMetrics fontMetrics;
145 if(0u != glyphMetrics.fontId)
147 mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
151 fontMetrics.ascender = glyphMetrics.fontHeight;
152 fontMetrics.descender = 0.f;
153 fontMetrics.height = fontMetrics.ascender;
154 fontMetrics.underlinePosition = 0.f;
155 fontMetrics.underlineThickness = 1.f;
158 // Sets the maximum ascender.
159 lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
161 // Sets the minimum descender.
162 lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
164 // Sets the line size
165 lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
166 lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
168 // Add the line spacing
169 lineLayout.lineSpacing += mDefaultLineSpacing;
173 * @brief Merges a temporary line layout into the line layout.
175 * @param[in,out] lineLayout The line layout.
176 * @param[in] tmpLineLayout A temporary line layout.
178 void MergeLineLayout(LineLayout& lineLayout,
179 const LineLayout& tmpLineLayout)
181 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
182 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
184 lineLayout.penX = tmpLineLayout.penX;
185 lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
187 lineLayout.length = tmpLineLayout.length;
188 lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
190 // Sets the maximum ascender.
191 lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
193 // Sets the minimum descender.
194 lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
197 void LayoutRightToLeft(const Parameters& parameters,
198 const BidirectionalLineInfoRun& bidirectionalLineInfo,
200 float& whiteSpaceLengthEndOfLine)
202 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
203 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
204 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
205 const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
207 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
208 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
210 CharacterIndex characterLogicalIndex = 0u;
211 CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
213 if(RTL == bidirectionalLineInfo.direction)
215 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
217 const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
219 whiteSpaceLengthEndOfLine += glyphInfo.advance;
221 ++characterLogicalIndex;
222 characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
226 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
228 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
229 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
230 lastGlyphOfParagraphPlusOne,
231 charactersPerGlyphBuffer);
233 GlyphMetrics glyphMetrics;
234 GetGlyphsMetrics(glyphIndex,
235 numberOfGLyphsInGroup,
240 float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
242 // Traverses the characters of the right to left paragraph.
243 for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
245 // Convert the character in the logical order into the character in the visual order.
246 const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
247 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
249 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
251 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
252 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
253 lastGlyphOfParagraphPlusOne,
254 charactersPerGlyphBuffer);
256 characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
258 GlyphMetrics glyphMetrics;
259 GetGlyphsMetrics(glyphIndex,
260 numberOfGLyphsInGroup,
267 if(RTL == bidirectionalLineInfo.direction)
269 length += glyphMetrics.advance;
273 whiteSpaceLengthEndOfLine += glyphMetrics.advance;
275 penX += glyphMetrics.advance;
279 if(LTR == bidirectionalLineInfo.direction)
281 whiteSpaceLengthEndOfLine = 0.f;
283 length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
284 penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
289 void ReorderBiDiLayout(const Parameters& parameters,
290 LayoutBidiParameters& bidiParameters,
291 const LineLayout& currentLineLayout,
292 LineLayout& lineLayout,
293 bool breakInCharacters)
295 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
297 // The last glyph to be laid-out.
298 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
300 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
302 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
303 if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
304 (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
306 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
308 // Sets the visual to logical map tables needed to reorder the text.
309 ReorderLine(bidirectionalParagraphInfo,
310 bidirectionalLinesInfo,
311 bidiParameters.bidiLineIndex,
312 lineLayout.characterIndex,
313 lineLayout.numberOfCharacters,
314 bidiParameters.paragraphDirection);
316 // Recalculate the length of the line and update the layout.
317 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
319 if(!bidirectionalLineInfo.isIdentity)
322 float whiteSpaceLengthEndOfLine = 0.f;
323 LayoutRightToLeft(parameters,
324 bidirectionalLineInfo,
326 whiteSpaceLengthEndOfLine);
328 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
329 if(!Equals(length, lineLayout.length))
331 const bool isMultiline = mLayout == MULTI_LINE_BOX;
333 if(isMultiline && (length > parameters.boundingBox.width))
335 if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
337 // The word doesn't fit in one line. It has to be split by character.
339 // Remove the last laid out glyph(s) as they doesn't fit.
340 for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
342 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
343 lastGlyphOfParagraphPlusOne,
344 charactersPerGlyphBuffer);
346 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
348 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
349 lineLayout.numberOfCharacters -= numberOfCharacters;
351 AdjustLayout(parameters,
353 bidirectionalParagraphInfo,
356 if(lineLayout.length < parameters.boundingBox.width)
361 if(glyphIndex < numberOfGLyphsInGroup)
363 // avoids go under zero for an unsigned int.
367 glyphIndex -= numberOfGLyphsInGroup;
372 lineLayout = currentLineLayout;
374 AdjustLayout(parameters,
376 bidirectionalParagraphInfo,
382 lineLayout.length = std::max(length, lineLayout.length);
389 void AdjustLayout(const Parameters& parameters,
390 LayoutBidiParameters& bidiParameters,
391 const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
392 LineLayout& lineLayout)
394 Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
396 // Remove current reordered line.
397 bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
399 // Re-build the conversion table without the removed glyphs.
400 ReorderLine(bidirectionalParagraphInfo,
401 bidirectionalLinesInfo,
402 bidiParameters.bidiLineIndex,
403 lineLayout.characterIndex,
404 lineLayout.numberOfCharacters,
405 bidiParameters.paragraphDirection);
407 const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
410 float whiteSpaceLengthEndOfLine = 0.f;
411 LayoutRightToLeft(parameters,
412 bidirectionalLineInfo,
414 whiteSpaceLengthEndOfLine);
416 lineLayout.length = length;
417 lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
421 * Retrieves the line layout for a given box width.
423 * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
424 * of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
425 * to reorder the line and recalculate its length.
428 * @param[in] parameters The layout parameters.
429 * @param[] bidiParameters Bidirectional info for the current line.
430 * @param[out] lineLayout The line layout.
431 * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
433 void GetLineLayoutForBox(const Parameters& parameters,
434 LayoutBidiParameters& bidiParameters,
435 LineLayout& lineLayout,
438 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
439 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " initial glyph index : %d\n", lineLayout.glyphIndex);
441 const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
442 const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
443 const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
444 const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
445 const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
447 const float outlineWidth = static_cast<float>(parameters.textModel->GetOutlineWidth());
448 const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
450 const bool isMultiline = mLayout == MULTI_LINE_BOX;
451 const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
453 // The last glyph to be laid-out.
454 const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
456 // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
457 // In the case the line starts with a right to left character, if the width is longer than the advance,
458 // the difference needs to be added to the line length.
460 // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
461 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
462 lastGlyphOfParagraphPlusOne,
463 charactersPerGlyphBuffer);
465 GlyphMetrics glyphMetrics;
466 GetGlyphsMetrics(lineLayout.glyphIndex,
467 numberOfGLyphsInGroup,
472 // Set the direction of the first character of the line.
473 lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
475 // Stores temporary line layout which has not been added to the final line layout.
476 LineLayout tmpLineLayout;
478 // Initialize the start point.
480 // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
481 // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
482 // 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.
483 tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
485 // Calculate the line height if there is no characters.
486 FontId lastFontId = glyphMetrics.fontId;
487 UpdateLineHeight(glyphMetrics, tmpLineLayout);
489 bool oneWordLaidOut = false;
491 for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
492 glyphIndex < lastGlyphOfParagraphPlusOne;)
494 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index : %d\n", glyphIndex);
496 // Check whether this glyph comes from a character that is shaped in multiple glyphs.
497 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
498 lastGlyphOfParagraphPlusOne,
499 charactersPerGlyphBuffer);
501 GlyphMetrics glyphMetrics;
502 GetGlyphsMetrics(glyphIndex,
503 numberOfGLyphsInGroup,
508 const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
510 // Check if the font of the current glyph is the same of the previous one.
511 // If it's different the ascender and descender need to be updated.
512 if(lastFontId != glyphMetrics.fontId)
514 UpdateLineHeight(glyphMetrics, tmpLineLayout);
515 lastFontId = glyphMetrics.fontId;
518 // Get the character indices for the current glyph. The last character index is needed
519 // because there are glyphs formed by more than one character but their break info is
520 // given only for the last character.
521 const Length charactersPerGlyph = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
522 const bool hasCharacters = charactersPerGlyph > 0u;
523 const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
524 const CharacterIndex characterLastIndex = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
526 // Get the line break info for the current character.
527 const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
529 // Increase the number of characters.
530 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
532 // Increase the number of glyphs.
533 tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
535 // Check whether is a white space.
536 const Character character = *(textBuffer + characterFirstIndex);
537 const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
539 // Calculate the length of the line.
541 // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
542 const float previousTmpPenX = tmpLineLayout.penX;
543 const float previousTmpAdvance = tmpLineLayout.previousAdvance;
544 const float previousTmpLength = tmpLineLayout.length;
545 const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
549 // Add the length to the length of white spaces at the end of the line.
550 tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
554 tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
555 tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
557 tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
559 // Clear the white space length at the end of the line.
560 tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
563 // Check if the accumulated length fits in the width of the box.
564 if((completelyFill || isMultiline) && !isWhiteSpace &&
565 (tmpLineLayout.length > parameters.boundingBox.width))
567 // Current word does not fit in the box's width.
568 if(!oneWordLaidOut || completelyFill)
570 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Break the word by character\n");
572 // The word doesn't fit in the control's width. It needs to be split by character.
573 if(tmpLineLayout.numberOfGlyphs > 0u)
575 tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
576 tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
578 tmpLineLayout.penX = previousTmpPenX;
579 tmpLineLayout.previousAdvance = previousTmpAdvance;
580 tmpLineLayout.length = previousTmpLength;
581 tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
584 // Add part of the word to the line layout.
585 MergeLineLayout(lineLayout, tmpLineLayout);
589 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Current word does not fit.\n");
592 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
594 // Reorder the RTL line.
595 if(bidiParameters.isBidirectional)
597 ReorderBiDiLayout(parameters,
607 if((isMultiline || isLastGlyph) &&
608 (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
610 LineLayout currentLineLayout = lineLayout;
612 // Must break the line. Update the line layout and return.
613 MergeLineLayout(lineLayout, tmpLineLayout);
615 // Reorder the RTL line.
616 if(bidiParameters.isBidirectional)
618 ReorderBiDiLayout(parameters,
625 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " Must break\n");
626 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
632 (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
634 oneWordLaidOut = isWordLaidOut;
635 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " One word laid-out\n");
637 // Current glyph is the last one of the current word.
638 // Add the temporal layout to the current one.
639 MergeLineLayout(lineLayout, tmpLineLayout);
641 tmpLineLayout.Clear();
644 glyphIndex += numberOfGLyphsInGroup;
647 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
650 void SetGlyphPositions(const GlyphInfo* const glyphsBuffer,
651 Length numberOfGlyphs,
653 float interGlyphExtraAdvance,
654 Vector2* glyphPositionsBuffer)
656 // Traverse the glyphs and set the positions.
658 // Check if the x bearing of the first character is negative.
659 // If it has a negative x bearing, it will exceed the boundaries of the actor,
660 // so the penX position needs to be moved to the right.
662 const GlyphInfo& glyph = *glyphsBuffer;
663 float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
665 for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
667 const GlyphInfo& glyph = *(glyphsBuffer + i);
668 Vector2& position = *(glyphPositionsBuffer + i);
670 position.x = penX + glyph.xBearing;
671 position.y = -glyph.yBearing;
673 penX += (glyph.advance + interGlyphExtraAdvance);
677 void SetGlyphPositions(const Parameters& layoutParameters,
678 Vector2* glyphPositionsBuffer,
679 LayoutBidiParameters& layoutBidiParameters,
680 const LineLayout& layout)
682 const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
683 const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
684 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
685 const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
686 const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
688 CharacterIndex characterLogicalIndex = 0u;
689 CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
692 while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
694 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
695 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
697 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
699 position.y = -glyph.yBearing;
701 penX += glyph.advance;
703 ++characterLogicalIndex;
704 characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
707 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
708 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
710 penX += -glyph.xBearing;
712 // Traverses the characters of the right to left paragraph.
713 for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
714 ++characterLogicalIndex)
716 // Convert the character in the logical order into the character in the visual order.
717 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
719 // Get the number of glyphs of the character.
720 const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
722 for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
724 // Convert the character in the visual order into the glyph in the visual order.
725 const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
727 DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
729 const GlyphInfo& glyph = *(glyphsBuffer + glyphIndex);
730 Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
732 position.x = penX + glyph.xBearing;
733 position.y = -glyph.yBearing;
735 penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
741 * @brief Resizes the line buffer.
743 * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
744 * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
745 * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
746 * @param[in] updateCurrentBuffer Whether the layout is updated.
748 * @return Pointer to either lines or newLines.
750 LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
751 Vector<LineRun>& newLines,
752 Length& linesCapacity,
753 bool updateCurrentBuffer)
755 LineRun* linesBuffer = nullptr;
756 // Reserve more space for the next lines.
758 if(updateCurrentBuffer)
760 newLines.Resize(linesCapacity);
761 linesBuffer = newLines.Begin();
765 lines.Resize(linesCapacity);
766 linesBuffer = lines.Begin();
773 * Ellipsis a line if it exceeds the width's of the bounding box.
775 * @param[in] layoutParameters The parameters needed to layout the text.
776 * @param[in] layout The line layout.
777 * @param[in,out] layoutSize The text's layout size.
778 * @param[in,out] linesBuffer Pointer to the line's buffer.
779 * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
780 * @param[in,out] numberOfLines The number of laid-out lines.
781 * @param[in] penY The vertical layout position.
782 * @param[in] currentParagraphDirection The current paragraph's direction.
783 * @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
785 * return Whether the line is ellipsized.
787 bool EllipsisLine(const Parameters& layoutParameters,
788 LayoutBidiParameters& layoutBidiParameters,
789 const LineLayout& layout,
791 LineRun* linesBuffer,
792 Vector2* glyphPositionsBuffer,
793 Length& numberOfLines,
795 bool& isAutoScrollEnabled)
797 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)));
801 isAutoScrollEnabled = false;
802 // Do not layout more lines if ellipsis is enabled.
804 // The last line needs to be completely filled with characters.
805 // Part of a word may be used.
807 LineRun* lineRun = nullptr;
808 LineLayout ellipsisLayout;
809 if(0u != numberOfLines)
811 // Get the last line and layout it again with the 'completelyFill' flag to true.
812 lineRun = linesBuffer + (numberOfLines - 1u);
814 penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
816 ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
820 // At least there is space reserved for one line.
821 lineRun = linesBuffer;
823 lineRun->glyphRun.glyphIndex = 0u;
824 ellipsisLayout.glyphIndex = 0u;
829 GetLineLayoutForBox(layoutParameters,
830 layoutBidiParameters,
834 lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
835 lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
836 lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
837 lineRun->width = ellipsisLayout.length;
838 lineRun->extraLength = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
839 lineRun->ascender = ellipsisLayout.ascender;
840 lineRun->descender = ellipsisLayout.descender;
841 lineRun->ellipsis = true;
843 layoutSize.width = layoutParameters.boundingBox.width;
844 if(layoutSize.height < Math::MACHINE_EPSILON_1000)
846 layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
849 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
850 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
852 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
854 if(layoutBidiParameters.isBidirectional)
856 layoutBidiParameters.bidiLineIndex = 0u;
857 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
858 endIt = bidirectionalLinesInfo.End();
860 ++it, ++layoutBidiParameters.bidiLineIndex)
862 const BidirectionalLineInfoRun& run = *it;
864 if(ellipsisLayout.characterIndex == run.characterRun.characterIndex)
866 // Found where to insert the bidi line info.
872 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
874 if((nullptr != bidirectionalLineInfo) &&
875 !bidirectionalLineInfo->isIdentity &&
876 (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
878 lineRun->direction = RTL;
879 SetGlyphPositions(layoutParameters,
880 glyphPositionsBuffer,
881 layoutBidiParameters,
886 lineRun->direction = LTR;
887 SetGlyphPositions(glyphsBuffer + lineRun->glyphRun.glyphIndex,
888 ellipsisLayout.numberOfGlyphs,
890 layoutParameters.interGlyphExtraAdvance,
891 glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex);
899 * @brief Updates the text layout with a new laid-out line.
901 * @param[in] layoutParameters The parameters needed to layout the text.
902 * @param[in] layout The line layout.
903 * @param[in,out] layoutSize The text's layout size.
904 * @param[in,out] linesBuffer Pointer to the line's buffer.
905 * @param[in] index Index to the vector of glyphs.
906 * @param[in,out] numberOfLines The number of laid-out lines.
907 * @param[in] isLastLine Whether the laid-out line is the last one.
909 void UpdateTextLayout(const Parameters& layoutParameters,
910 const LineLayout& layout,
912 LineRun* linesBuffer,
914 Length& numberOfLines,
917 LineRun& lineRun = *(linesBuffer + numberOfLines);
920 lineRun.glyphRun.glyphIndex = index;
921 lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
922 lineRun.characterRun.characterIndex = layout.characterIndex;
923 lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
924 lineRun.width = layout.length;
925 lineRun.extraLength = std::ceil(layout.whiteSpaceLengthEndOfLine);
927 // Rounds upward to avoid a non integer size.
928 lineRun.width = std::ceil(lineRun.width);
930 lineRun.ascender = layout.ascender;
931 lineRun.descender = layout.descender;
932 lineRun.direction = layout.direction;
933 lineRun.ellipsis = false;
935 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
936 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
938 lineRun.lineSpacing += mDefaultLineSpacing;
940 // Update the actual size.
941 if(lineRun.width > layoutSize.width)
943 layoutSize.width = lineRun.width;
946 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
950 * @brief Updates the text layout with the last laid-out line.
952 * @param[in] layoutParameters The parameters needed to layout the text.
953 * @param[in] characterIndex The character index of the line.
954 * @param[in] glyphIndex The glyph index of the line.
955 * @param[in,out] layoutSize The text's layout size.
956 * @param[in,out] linesBuffer Pointer to the line's buffer.
957 * @param[in,out] numberOfLines The number of laid-out lines.
959 void UpdateTextLayout(const Parameters& layoutParameters,
960 CharacterIndex characterIndex,
961 GlyphIndex glyphIndex,
963 LineRun* linesBuffer,
964 Length& numberOfLines)
966 const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
968 // Need to add a new line with no characters but with height to increase the layoutSize.height
969 const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
971 Text::FontMetrics fontMetrics;
972 if(0u != glyphInfo.fontId)
974 mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
977 LineRun& lineRun = *(linesBuffer + numberOfLines);
980 lineRun.glyphRun.glyphIndex = glyphIndex;
981 lineRun.glyphRun.numberOfGlyphs = 0u;
982 lineRun.characterRun.characterIndex = characterIndex;
983 lineRun.characterRun.numberOfCharacters = 0u;
985 lineRun.ascender = fontMetrics.ascender;
986 lineRun.descender = fontMetrics.descender;
987 lineRun.extraLength = 0.f;
988 lineRun.alignmentOffset = 0.f;
989 lineRun.direction = LTR;
990 lineRun.ellipsis = false;
992 lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
993 lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
995 lineRun.lineSpacing += mDefaultLineSpacing;
997 layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
1001 * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1003 * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1004 * @param[in,out] layoutSize The text's layout size.
1006 void UpdateLayoutSize(const Vector<LineRun>& lines,
1009 for(Vector<LineRun>::ConstIterator it = lines.Begin(),
1010 endIt = lines.End();
1014 const LineRun& line = *it;
1016 if(line.width > layoutSize.width)
1018 layoutSize.width = line.width;
1021 layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
1026 * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1028 * @param[in] layoutParameters The parameters needed to layout the text.
1029 * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1030 * @param[in] characterOffset The offset to be added to the runs of characters.
1031 * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1033 void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1034 Vector<LineRun>& lines,
1035 Length characterOffset,
1038 // Update the glyph and character runs.
1039 for(Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1040 endIt = lines.End();
1044 LineRun& line = *it;
1046 line.glyphRun.glyphIndex = glyphOffset;
1047 line.characterRun.characterIndex = characterOffset;
1049 glyphOffset += line.glyphRun.numberOfGlyphs;
1050 characterOffset += line.characterRun.numberOfCharacters;
1054 bool LayoutText(Parameters& layoutParameters,
1056 bool elideTextEnabled,
1057 bool& isAutoScrollEnabled)
1059 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1060 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1062 Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1064 if(0u == layoutParameters.numberOfGlyphs)
1066 // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1067 if(layoutParameters.isLastNewParagraph)
1069 Length numberOfLines = lines.Count();
1070 if(0u != numberOfLines)
1072 const LineRun& lastLine = *(lines.End() - 1u);
1074 if(0u != lastLine.characterRun.numberOfCharacters)
1076 // Need to add a new line with no characters but with height to increase the layoutSize.height
1078 Initialize(newLine);
1079 lines.PushBack(newLine);
1081 UpdateTextLayout(layoutParameters,
1082 lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1083 lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1091 // Calculates the layout size.
1092 UpdateLayoutSize(lines,
1095 // Rounds upward to avoid a non integer size.
1096 layoutSize.height = std::ceil(layoutSize.height);
1098 // Nothing else do if there are no glyphs to layout.
1102 const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1103 const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1104 Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1106 // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1107 // This extra line needs to be removed.
1108 if(0u != lines.Count())
1110 Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1112 if((0u == lastLine->characterRun.numberOfCharacters) &&
1113 (lastGlyphPlusOne == totalNumberOfGlyphs))
1115 lines.Remove(lastLine);
1119 // Retrieve BiDi info.
1120 const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1122 const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1123 const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1124 const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1126 // Set the layout bidirectional paramters.
1127 LayoutBidiParameters layoutBidiParameters;
1129 // Whether the layout is being updated or set from scratch.
1130 const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1132 Vector2* glyphPositionsBuffer = nullptr;
1133 Vector<Vector2> newGlyphPositions;
1135 LineRun* linesBuffer = nullptr;
1136 Vector<LineRun> newLines;
1138 // Estimate the number of lines.
1139 Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1140 Length numberOfLines = 0u;
1142 if(updateCurrentBuffer)
1144 newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1145 glyphPositionsBuffer = newGlyphPositions.Begin();
1147 newLines.Resize(linesCapacity);
1148 linesBuffer = newLines.Begin();
1152 glyphPositionsBuffer = glyphPositions.Begin();
1154 lines.Resize(linesCapacity);
1155 linesBuffer = lines.Begin();
1158 float penY = CalculateLineOffset(lines,
1159 layoutParameters.startLineIndex);
1160 for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1162 layoutBidiParameters.Clear();
1164 if(hasBidiParagraphs)
1166 const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1168 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1169 endIt = bidirectionalParagraphsInfo.End();
1171 ++it, ++layoutBidiParameters.bidiParagraphIndex)
1173 const BidirectionalParagraphInfoRun& run = *it;
1175 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1177 if(lastCharacterIndex <= startCharacterIndex)
1179 // Do not process, the paragraph has already been processed.
1183 if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1185 layoutBidiParameters.paragraphDirection = run.direction;
1186 layoutBidiParameters.isBidirectional = true;
1189 // Has already been found.
1193 if(layoutBidiParameters.isBidirectional)
1195 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1196 endIt = bidirectionalLinesInfo.End();
1198 ++it, ++layoutBidiParameters.bidiLineIndex)
1200 const BidirectionalLineInfoRun& run = *it;
1202 const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1204 if(lastCharacterIndex <= startCharacterIndex)
1210 if(startCharacterIndex < lastCharacterIndex)
1212 // Found where to insert the bidi line info.
1219 CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1221 // Get the layout for the line.
1223 layout.direction = layoutBidiParameters.paragraphDirection;
1224 layout.glyphIndex = index;
1225 GetLineLayoutForBox(layoutParameters,
1226 layoutBidiParameters,
1230 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " glyph index %d\n", layout.glyphIndex);
1231 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " character index %d\n", layout.characterIndex);
1232 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of glyphs %d\n", layout.numberOfGlyphs);
1233 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " number of characters %d\n", layout.numberOfCharacters);
1234 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " length %f\n", layout.length);
1236 if(0u == layout.numberOfGlyphs)
1238 // The width is too small and no characters are laid-out.
1239 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1241 lines.Resize(numberOfLines);
1243 // Rounds upward to avoid a non integer size.
1244 layoutSize.height = std::ceil(layoutSize.height);
1249 // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1251 penY += layout.ascender;
1253 DALI_LOG_INFO(gLogFilter, Debug::Verbose, " pen y %f\n", penY);
1255 bool ellipsis = false;
1256 if(elideTextEnabled)
1258 layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1260 // Does the ellipsis of the last line.
1261 ellipsis = EllipsisLine(layoutParameters,
1262 layoutBidiParameters,
1266 glyphPositionsBuffer,
1269 isAutoScrollEnabled);
1274 // No more lines to layout.
1279 // Whether the last line has been laid-out.
1280 const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1282 if(numberOfLines == linesCapacity)
1284 // Reserve more space for the next lines.
1285 linesBuffer = ResizeLinesBuffer(lines,
1288 updateCurrentBuffer);
1291 // Updates the current text's layout with the line's layout.
1292 UpdateTextLayout(layoutParameters,
1300 const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1302 if((nextIndex == totalNumberOfGlyphs) &&
1303 layoutParameters.isLastNewParagraph &&
1304 (mLayout == MULTI_LINE_BOX))
1306 // The last character of the text is a new paragraph character.
1307 // An extra line with no characters is added to increase the text's height
1308 // in order to place the cursor.
1310 if(numberOfLines == linesCapacity)
1312 // Reserve more space for the next lines.
1313 linesBuffer = ResizeLinesBuffer(lines,
1316 updateCurrentBuffer);
1319 UpdateTextLayout(layoutParameters,
1320 layout.characterIndex + layout.numberOfCharacters,
1321 index + layout.numberOfGlyphs,
1325 } // whether to add a last line.
1327 const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1328 const float outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1330 const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1332 if((nullptr != bidirectionalLineInfo) &&
1333 !bidirectionalLineInfo->isIdentity &&
1334 (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1336 SetGlyphPositions(layoutParameters,
1337 glyphPositionsBuffer,
1338 layoutBidiParameters,
1343 // Sets the positions of the glyphs.
1344 SetGlyphPositions(glyphsBuffer + index,
1345 layout.numberOfGlyphs,
1347 layoutParameters.interGlyphExtraAdvance,
1348 glyphPositionsBuffer + index - layoutParameters.startGlyphIndex);
1351 // Updates the vertical pen's position.
1352 penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1353 // If there is a defaultLineSize, updates the pen's position.
1354 if(mDefaultLineSize > 0.f)
1356 float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1357 lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
1358 penY += lineSpacing;
1361 // Increase the glyph index.
1364 } // end for() traversing glyphs.
1366 if(updateCurrentBuffer)
1368 glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1369 newGlyphPositions.Begin(),
1370 newGlyphPositions.End());
1371 glyphPositions.Resize(totalNumberOfGlyphs);
1373 newLines.Resize(numberOfLines);
1375 // Current text's layout size adds only the newly laid-out lines.
1376 // Updates the layout size with the previously laid-out lines.
1377 UpdateLayoutSize(lines,
1380 if(0u != newLines.Count())
1382 const LineRun& lastLine = *(newLines.End() - 1u);
1384 const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1385 const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1387 // Update the indices of the runs before the new laid-out lines are inserted.
1388 UpdateLineIndexOffsets(layoutParameters,
1393 // Insert the lines.
1394 lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1401 lines.Resize(numberOfLines);
1404 // Rounds upward to avoid a non integer size.
1405 layoutSize.height = std::ceil(layoutSize.height);
1407 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1412 void Align(const Size& size,
1413 CharacterIndex startIndex,
1414 Length numberOfCharacters,
1415 Text::HorizontalAlignment::Type horizontalAlignment,
1416 Vector<LineRun>& lines,
1417 float& alignmentOffset,
1418 Dali::LayoutDirection::Type layoutDirection,
1419 bool matchSystemLanguageDirection)
1421 const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1423 alignmentOffset = MAX_FLOAT;
1424 // Traverse all lines and align the glyphs.
1425 for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1429 LineRun& line = *it;
1431 if(line.characterRun.characterIndex < startIndex)
1433 // Do not align lines which have already been aligned.
1437 if(line.characterRun.characterIndex > lastCharacterPlusOne)
1439 // Do not align lines beyond the last laid-out character.
1443 if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1445 // Do not align lines beyond the last laid-out character unless the line is last and empty.
1449 // Calculate the line's alignment offset accordingly with the align option,
1450 // the box width, line length, and the paragraph's direction.
1451 CalculateHorizontalAlignment(size.width,
1452 horizontalAlignment,
1455 matchSystemLanguageDirection);
1457 // Updates the alignment offset.
1458 alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1462 void CalculateHorizontalAlignment(float boxWidth,
1463 HorizontalAlignment::Type horizontalAlignment,
1465 Dali::LayoutDirection::Type layoutDirection,
1466 bool matchSystemLanguageDirection)
1468 line.alignmentOffset = 0.f;
1469 const bool isLineRTL = RTL == line.direction;
1471 // Whether to swap the alignment.
1472 // 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.
1473 bool isLayoutRTL = isLineRTL;
1474 float lineLength = line.width;
1476 // match align for system language direction
1477 if(matchSystemLanguageDirection)
1479 // Swap the alignment type if the line is right to left.
1480 isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1482 // Calculate the horizontal line offset.
1483 switch(horizontalAlignment)
1485 case HorizontalAlignment::BEGIN:
1491 lineLength += line.extraLength;
1494 line.alignmentOffset = boxWidth - lineLength;
1498 line.alignmentOffset = 0.f;
1502 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1503 line.alignmentOffset -= line.extraLength;
1508 case HorizontalAlignment::CENTER:
1510 line.alignmentOffset = 0.5f * (boxWidth - lineLength);
1514 line.alignmentOffset -= line.extraLength;
1517 line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
1520 case HorizontalAlignment::END:
1524 line.alignmentOffset = 0.f;
1528 // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1529 line.alignmentOffset -= line.extraLength;
1536 lineLength += line.extraLength;
1539 line.alignmentOffset = boxWidth - lineLength;
1546 void Initialize(LineRun& line)
1548 line.glyphRun.glyphIndex = 0u;
1549 line.glyphRun.numberOfGlyphs = 0u;
1550 line.characterRun.characterIndex = 0u;
1551 line.characterRun.numberOfCharacters = 0u;
1553 line.ascender = 0.f;
1554 line.descender = 0.f;
1555 line.extraLength = 0.f;
1556 line.alignmentOffset = 0.f;
1557 line.direction = LTR;
1558 line.ellipsis = false;
1559 line.lineSpacing = mDefaultLineSpacing;
1564 float mDefaultLineSpacing;
1565 float mDefaultLineSize;
1567 IntrusivePtr<Metrics> mMetrics;
1573 mImpl = new Engine::Impl();
1581 void Engine::SetMetrics(MetricsPtr& metrics)
1583 mImpl->mMetrics = metrics;
1586 void Engine::SetLayout(Type layout)
1588 mImpl->mLayout = layout;
1591 Engine::Type Engine::GetLayout() const
1593 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1594 return mImpl->mLayout;
1597 void Engine::SetCursorWidth(int width)
1599 mImpl->mCursorWidth = static_cast<float>(width);
1602 int Engine::GetCursorWidth() const
1604 return static_cast<int>(mImpl->mCursorWidth);
1607 bool Engine::LayoutText(Parameters& layoutParameters,
1609 bool elideTextEnabled,
1610 bool& isAutoScrollEnabled)
1612 return mImpl->LayoutText(layoutParameters,
1615 isAutoScrollEnabled);
1618 void Engine::Align(const Size& size,
1619 CharacterIndex startIndex,
1620 Length numberOfCharacters,
1621 Text::HorizontalAlignment::Type horizontalAlignment,
1622 Vector<LineRun>& lines,
1623 float& alignmentOffset,
1624 Dali::LayoutDirection::Type layoutDirection,
1625 bool matchSystemLanguageDirection)
1630 horizontalAlignment,
1634 matchSystemLanguageDirection);
1637 void Engine::SetDefaultLineSpacing(float lineSpacing)
1639 mImpl->mDefaultLineSpacing = lineSpacing;
1642 float Engine::GetDefaultLineSpacing() const
1644 return mImpl->mDefaultLineSpacing;
1647 void Engine::SetDefaultLineSize(float lineSize)
1649 mImpl->mDefaultLineSize = lineSize;
1652 float Engine::GetDefaultLineSize() const
1654 return mImpl->mDefaultLineSize;
1657 } // namespace Layout
1661 } // namespace Toolkit