2 * Copyright (c) 2015 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/public-api/math/vector2.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
26 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
27 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
39 * @brief Stores temporary layout info of the line.
43 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
44 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
45 Length numberOfCharacters; ///< The number of characters which fit in one line.
46 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
47 float length; ///< The length of the glyphs which fit in one line.
48 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
49 float height; ///< The maximum height of all fonts in the line.
50 float ascender; ///< The maximum ascender of all fonts in the line.
53 struct LayoutEngine::Impl
56 : mLayout( LayoutEngine::SINGLE_LINE_BOX )
58 mFontClient = TextAbstraction::FontClient::Get();
62 * Retrieves the line layout for a given box width.
64 void GetLineLayoutForBox( const LayoutParameters& parameters,
65 LineLayout& lineLayout )
67 // Initializes the line layout.
68 lineLayout.numberOfCharacters = 0u;
69 lineLayout.numberOfGlyphs = 0u;
70 lineLayout.length = 0.f;
71 lineLayout.wsLengthEndOfLine = 0.f;
72 lineLayout.height = 0.f;
73 lineLayout.ascender = 0.f;
75 // Get the last glyph index.
76 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
78 FontId lastFontId = 0u;
79 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
80 glyphIndex < parameters.totalNumberOfGlyphs;
83 // Get the glyph info.
84 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
86 // Get the character indices for the current glyph. The last character index is needed
87 // because there are glyphs formed by more than one character but their break info is
88 // given only for the last character.
89 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
91 // Increase the number of characters.
92 lineLayout.numberOfCharacters += charactersPerGlyph;
94 // Increase the number of glyphs.
95 lineLayout.numberOfGlyphs++;
97 // Increase the accumulated length.
98 lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
100 if( lastFontId != glyphInfo.fontId )
102 Text::FontMetrics fontMetrics;
103 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
105 // Sets the maximum height.
106 if( fontMetrics.height > lineLayout.height )
108 lineLayout.height = fontMetrics.height;
111 // Sets the maximum ascender.
112 if( fontMetrics.ascender > lineLayout.ascender )
114 lineLayout.ascender = fontMetrics.ascender;
117 lastFontId = glyphInfo.fontId;
123 * Retrieves the line layout for a given box width.
125 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
126 LineLayout& lineLayout )
128 // Initializes the line layout.
129 lineLayout.numberOfCharacters = 0u;
130 lineLayout.numberOfGlyphs = 0u;
131 lineLayout.length = 0.f;
132 lineLayout.wsLengthEndOfLine = 0.f;
133 lineLayout.height = 0.f;
134 lineLayout.ascender = 0.f;
136 // Stores temporary line layout which has not been added to the final line layout.
137 LineLayout tmpLineLayout;
138 tmpLineLayout.numberOfCharacters = 0u;
139 tmpLineLayout.numberOfGlyphs = 0u;
140 tmpLineLayout.length = 0.f;
141 tmpLineLayout.wsLengthEndOfLine = 0.f;
142 tmpLineLayout.height = 0.f;
143 tmpLineLayout.ascender = 0.f;
145 // Get the last glyph index.
146 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
148 FontId lastFontId = 0u;
149 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
150 glyphIndex < parameters.totalNumberOfGlyphs;
153 // Get the glyph info.
154 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
156 // Get the character indices for the current glyph. The last character index is needed
157 // because there are glyphs formed by more than one character but their break info is
158 // given only for the last character.
159 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
160 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
161 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
163 // Get the line break info for the current character.
164 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
166 // Get the word break info for the current character.
167 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
169 // Increase the number of characters.
170 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
172 // Increase the number of glyphs.
173 tmpLineLayout.numberOfGlyphs++;
175 // Increase the accumulated length.
176 tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
178 // Check if the accumulated length fits in the width of the box.
179 if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
181 // Current word does not fit in the box's width.
185 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
187 if( glyphIndex == lastGlyphIndex )
189 // Must break the line. Update the line layout and return.
190 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
191 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
192 lineLayout.length += tmpLineLayout.length;
193 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
195 if( tmpLineLayout.height > lineLayout.height )
197 lineLayout.height = tmpLineLayout.height;
200 if( tmpLineLayout.ascender > lineLayout.ascender )
202 lineLayout.ascender = tmpLineLayout.ascender;
206 tmpLineLayout.numberOfCharacters = 0u;
207 tmpLineLayout.numberOfGlyphs = 0u;
208 tmpLineLayout.length = 0u;
209 tmpLineLayout.wsLengthEndOfLine = 0u;
210 tmpLineLayout.height = 0.f;
211 tmpLineLayout.ascender = 0.f;
215 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
217 // Current glyph is the last one of the current word.
218 // Add the temporal layout to the current one.
219 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
220 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
221 lineLayout.length += tmpLineLayout.length;
222 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
224 if( tmpLineLayout.height > lineLayout.height )
226 lineLayout.height = tmpLineLayout.height;
229 if( tmpLineLayout.ascender > lineLayout.ascender )
231 lineLayout.ascender = tmpLineLayout.ascender;
234 tmpLineLayout.numberOfCharacters = 0u;
235 tmpLineLayout.numberOfGlyphs = 0u;
236 tmpLineLayout.length = 0u;
237 tmpLineLayout.wsLengthEndOfLine = 0u;
238 tmpLineLayout.height = 0.f;
239 tmpLineLayout.ascender = 0.f;
242 if( lastFontId != glyphInfo.fontId )
244 Text::FontMetrics fontMetrics;
245 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
247 // Sets the maximum height.
248 if( fontMetrics.height > tmpLineLayout.height )
250 tmpLineLayout.height = fontMetrics.height;
253 // Sets the maximum ascender.
254 if( fontMetrics.ascender > tmpLineLayout.ascender )
256 tmpLineLayout.ascender = fontMetrics.ascender;
259 lastFontId = glyphInfo.fontId;
264 bool LayoutText( const LayoutParameters& layoutParameters,
265 Vector<Vector2>& glyphPositions,
266 Vector<LineRun>& lines,
269 // TODO Switch between different layouts
274 case LayoutEngine::SINGLE_LINE_BOX:
276 update = SingleLineLayout( layoutParameters,
282 case LayoutEngine::MULTI_LINE_BOX:
284 update = MultiLineLayout( layoutParameters,
297 void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
298 Vector<Vector2>& glyphPositions )
300 for( Length lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
302 const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex );
306 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
308 for( CharacterIndex characterLogicalIndex = 0u;
309 characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
310 ++characterLogicalIndex )
312 // Convert the character in the logical order into the character in the visual order.
313 const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
315 // Get the number of glyphs of the character.
316 const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
318 for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
320 // Convert the character in the visual order into the glyph in the visual order.
321 GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs;
323 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
324 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
326 position.x = penX + glyph.xBearing;
328 penX += glyph.advance;
334 bool SingleLineLayout( const LayoutParameters& layoutParameters,
335 Vector<Vector2>& glyphPositions,
336 Vector<LineRun>& lines,
340 layout.glyphIndex = 0u;
341 GetLineLayoutForBox( layoutParameters,
344 // Create a line run and add it to the lines.
345 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
348 lineRun.glyphIndex = 0u;
349 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
350 lineRun.characterRun.characterIndex = 0u;
351 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
352 lineRun.lineSize.width = layout.length;
353 lineRun.lineSize.height = layout.height;
355 lines.PushBack( lineRun );
357 // Update the actual size.
358 actualSize.width = layout.length;
359 actualSize.height = layout.height;
362 float penY = layout.height;
364 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
365 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
367 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
368 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
370 position.x = penX + glyph.xBearing;
371 position.y = penY - glyph.yBearing;
373 penX += glyph.advance;
379 bool MultiLineLayout( const LayoutParameters& layoutParameters,
380 Vector<Vector2>& glyphPositions,
381 Vector<LineRun>& lines,
385 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
389 // Get the layout for the line.
391 layout.glyphIndex = index;
392 GetMultiLineLayoutForBox( layoutParameters,
395 if( 0u == layout.numberOfGlyphs )
397 // The width is too small and no characters are laid-out.
401 // Create a line run and add it to the lines.
402 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
405 lineRun.glyphIndex = index;
406 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
407 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
408 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
409 lineRun.lineSize.width = layout.length;
410 lineRun.lineSize.height = layout.height;
412 lines.PushBack( lineRun );
414 // Update the actual size.
415 if( layout.length > actualSize.width )
417 actualSize.width = layout.length;
420 actualSize.height += layout.height;
422 // Traverse the glyphs and set the positions.
424 penY += layout.height;
426 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
427 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
429 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
430 Vector2& position = *( glyphPositionsBuffer + i );
432 position.x = penX + glyph.xBearing;
433 position.y = penY - glyph.yBearing;
435 penX += glyph.advance;
438 // Increase the glyph index.
439 index += layout.numberOfGlyphs;
445 LayoutEngine::Layout mLayout;
447 TextAbstraction::FontClient mFontClient;
450 LayoutEngine::LayoutEngine()
453 mImpl = new LayoutEngine::Impl();
456 LayoutEngine::~LayoutEngine()
461 void LayoutEngine::SetLayout( Layout layout )
463 mImpl->mLayout = layout;
466 unsigned int LayoutEngine::GetLayout() const
468 return mImpl->mLayout;
471 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
472 Vector<Vector2>& glyphPositions,
473 Vector<LineRun>& lines,
476 return mImpl->LayoutText( layoutParameters,
482 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
483 Vector<Vector2>& glyphPositions )
485 mImpl->ReLayoutRightToLeftLines( layoutParameters,
491 } // namespace Toolkit