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>
38 * @brief Stores temporary layout info of the line.
42 GlyphIndex glyphIndex; ///< Index of the first glyph to be laid-out.
43 CharacterIndex characterIndex; ///< Index of the first character to be laid-out.
44 Length numberOfCharacters; ///< The number of characters which fit in one line.
45 Length numberOfGlyphs; ///< The number of glyph which fit in one line.
46 float length; ///< The length of the glyphs which fit in one line.
47 float wsLengthEndOfLine; ///< The length of the white spaces at the end of the line.
48 float height; ///< The maximum height of all fonts in the line.
49 float ascender; ///< The maximum ascender of all fonts in the line.
52 struct LayoutEngine::Impl
55 : mLayout( LayoutEngine::SINGLE_LINE_BOX )
57 mFontClient = TextAbstraction::FontClient::Get();
61 * Retrieves the line layout for a given box width.
63 void GetLineLayoutForBox( const LayoutParameters& parameters,
64 LineLayout& lineLayout )
66 // Initializes the line layout.
67 lineLayout.numberOfCharacters = 0u;
68 lineLayout.numberOfGlyphs = 0u;
69 lineLayout.length = 0.f;
70 lineLayout.wsLengthEndOfLine = 0.f;
71 lineLayout.height = 0.f;
72 lineLayout.ascender = 0.f;
74 // Get the last glyph index.
75 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
77 FontId lastFontId = 0u;
78 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
79 glyphIndex < parameters.totalNumberOfGlyphs;
82 // Get the glyph info.
83 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
85 // Get the character indices for the current glyph. The last character index is needed
86 // because there are glyphs formed by more than one character but their break info is
87 // given only for the last character.
88 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
90 // Increase the number of characters.
91 lineLayout.numberOfCharacters += charactersPerGlyph;
93 // Increase the number of glyphs.
94 lineLayout.numberOfGlyphs++;
96 // Increase the accumulated length.
97 lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
99 if( lastFontId != glyphInfo.fontId )
101 Text::FontMetrics fontMetrics;
102 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
104 // Sets the maximum height.
105 if( fontMetrics.height > lineLayout.height )
107 lineLayout.height = fontMetrics.height;
110 // Sets the maximum ascender.
111 if( fontMetrics.ascender > lineLayout.ascender )
113 lineLayout.ascender = fontMetrics.ascender;
116 lastFontId = glyphInfo.fontId;
122 * Retrieves the line layout for a given box width.
124 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
125 LineLayout& lineLayout )
127 // Initializes the line layout.
128 lineLayout.numberOfCharacters = 0u;
129 lineLayout.numberOfGlyphs = 0u;
130 lineLayout.length = 0.f;
131 lineLayout.wsLengthEndOfLine = 0.f;
132 lineLayout.height = 0.f;
133 lineLayout.ascender = 0.f;
135 // Stores temporary line layout which has not been added to the final line layout.
136 LineLayout tmpLineLayout;
137 tmpLineLayout.numberOfCharacters = 0u;
138 tmpLineLayout.numberOfGlyphs = 0u;
139 tmpLineLayout.length = 0.f;
140 tmpLineLayout.wsLengthEndOfLine = 0.f;
141 tmpLineLayout.height = 0.f;
142 tmpLineLayout.ascender = 0.f;
144 // Get the last glyph index.
145 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
147 FontId lastFontId = 0u;
148 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
149 glyphIndex < parameters.totalNumberOfGlyphs;
152 // Get the glyph info.
153 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
155 // Get the character indices for the current glyph. The last character index is needed
156 // because there are glyphs formed by more than one character but their break info is
157 // given only for the last character.
158 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
159 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
160 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
162 // Get the line break info for the current character.
163 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
165 // Get the word break info for the current character.
166 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
168 // Increase the number of characters.
169 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
171 // Increase the number of glyphs.
172 tmpLineLayout.numberOfGlyphs++;
174 // Increase the accumulated length.
175 tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
177 // Check if the accumulated length fits in the width of the box.
178 if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
180 // Current word does not fit in the box's width.
184 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
186 if( glyphIndex == lastGlyphIndex )
188 // Must break the line. Update the line layout and return.
189 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
190 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
191 lineLayout.length += tmpLineLayout.length;
192 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
194 if( tmpLineLayout.height > lineLayout.height )
196 lineLayout.height = tmpLineLayout.height;
199 if( tmpLineLayout.ascender > lineLayout.ascender )
201 lineLayout.ascender = tmpLineLayout.ascender;
205 tmpLineLayout.numberOfCharacters = 0u;
206 tmpLineLayout.numberOfGlyphs = 0u;
207 tmpLineLayout.length = 0u;
208 tmpLineLayout.wsLengthEndOfLine = 0u;
209 tmpLineLayout.height = 0.f;
210 tmpLineLayout.ascender = 0.f;
214 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
216 // Current glyph is the last one of the current word.
217 // Add the temporal layout to the current one.
218 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
219 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
220 lineLayout.length += tmpLineLayout.length;
221 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
223 if( tmpLineLayout.height > lineLayout.height )
225 lineLayout.height = tmpLineLayout.height;
228 if( tmpLineLayout.ascender > lineLayout.ascender )
230 lineLayout.ascender = tmpLineLayout.ascender;
233 tmpLineLayout.numberOfCharacters = 0u;
234 tmpLineLayout.numberOfGlyphs = 0u;
235 tmpLineLayout.length = 0u;
236 tmpLineLayout.wsLengthEndOfLine = 0u;
237 tmpLineLayout.height = 0.f;
238 tmpLineLayout.ascender = 0.f;
241 if( lastFontId != glyphInfo.fontId )
243 Text::FontMetrics fontMetrics;
244 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
246 // Sets the maximum height.
247 if( fontMetrics.height > tmpLineLayout.height )
249 tmpLineLayout.height = fontMetrics.height;
252 // Sets the maximum ascender.
253 if( fontMetrics.ascender > tmpLineLayout.ascender )
255 tmpLineLayout.ascender = fontMetrics.ascender;
258 lastFontId = glyphInfo.fontId;
263 bool LayoutText( const LayoutParameters& layoutParameters,
264 Vector<Vector2>& glyphPositions,
265 Vector<LineRun>& lines,
268 // TODO Switch between different layouts
273 case LayoutEngine::SINGLE_LINE_BOX:
275 update = SingleLineLayout( layoutParameters,
281 case LayoutEngine::MULTI_LINE_BOX:
283 update = MultiLineLayout( layoutParameters,
296 // TODO - Rewrite this to handle bidi
297 bool SingleLineLayout( const LayoutParameters& layoutParameters,
298 Vector<Vector2>& glyphPositions,
299 Vector<LineRun>& lines,
303 layout.glyphIndex = 0u;
304 GetLineLayoutForBox( layoutParameters,
307 // Create a line run and add it to the lines.
308 const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
311 lineRun.glyphIndex = 0u;
312 lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
313 lineRun.characterRun.characterIndex = 0u;
314 lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
315 lineRun.lineSize.width = layout.length;
316 lineRun.lineSize.height = layout.height;
318 lines.PushBack( lineRun );
320 // Update the actual size.
321 actualSize.width = layout.length;
322 actualSize.height = layout.height;
325 float penY = layout.height;
327 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
328 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
330 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
331 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
333 position.x = penX + glyph.xBearing;
334 position.y = penY - glyph.yBearing;
336 penX += glyph.advance;
342 // TODO - Rewrite this to handle bidi
343 bool MultiLineLayout( const LayoutParameters& layoutParameters,
344 Vector<Vector2>& glyphPositions,
345 Vector<LineRun>& lines,
349 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
353 // Get the layout for the line.
355 layout.glyphIndex = index;
356 GetMultiLineLayoutForBox( layoutParameters,
359 if( 0u == layout.numberOfGlyphs )
361 // The width is too small and no characters are laid-out.
365 // Create a line run and add it to the lines.
366 const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
369 lineRun.glyphIndex = index;
370 lineRun.numberOfGlyphs = layout.numberOfGlyphs;
371 lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
372 lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
373 lineRun.lineSize.width = layout.length;
374 lineRun.lineSize.height = layout.height;
376 lines.PushBack( lineRun );
378 // Update the actual size.
379 if( layout.length > actualSize.width )
381 actualSize.width = layout.length;
384 actualSize.height += layout.height;
386 // Traverse the glyphs and set the positions.
388 penY += layout.height;
390 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
391 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
393 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
394 Vector2& position = *( glyphPositionsBuffer + i );
396 position.x = penX + glyph.xBearing;
397 position.y = penY - glyph.yBearing;
399 penX += glyph.advance;
402 // Increase the glyph index.
403 index += layout.numberOfGlyphs;
409 LayoutEngine::Layout mLayout;
411 TextAbstraction::FontClient mFontClient;
414 LayoutEngine::LayoutEngine()
417 mImpl = new LayoutEngine::Impl();
420 LayoutEngine::~LayoutEngine()
425 void LayoutEngine::SetLayout( Layout layout )
427 mImpl->mLayout = layout;
430 unsigned int LayoutEngine::GetLayout() const
432 return mImpl->mLayout;
435 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
436 Vector<Vector2>& glyphPositions,
437 Vector<LineRun>& lines,
440 return mImpl->LayoutText( layoutParameters,
448 } // namespace Toolkit