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 );
89 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
90 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
92 // Get the line break info for the current character.
93 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
95 // Get the word break info for the current character.
96 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
98 // Increase the number of characters.
99 lineLayout.numberOfCharacters += charactersPerGlyph;
101 // Increase the number of glyphs.
102 lineLayout.numberOfGlyphs++;
104 // Increase the accumulated length.
105 lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
107 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
110 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
114 if( lastFontId != glyphInfo.fontId )
116 Text::FontMetrics fontMetrics;
117 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
119 // Sets the maximum height.
120 if( fontMetrics.height > lineLayout.height )
122 lineLayout.height = fontMetrics.height;
125 // Sets the maximum ascender.
126 if( fontMetrics.ascender > lineLayout.ascender )
128 lineLayout.ascender = fontMetrics.ascender;
131 lastFontId = glyphInfo.fontId;
137 * Retrieves the line layout for a given box width.
139 void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
140 LineLayout& lineLayout )
142 // Initializes the line layout.
143 lineLayout.numberOfCharacters = 0u;
144 lineLayout.numberOfGlyphs = 0u;
145 lineLayout.length = 0.f;
146 lineLayout.wsLengthEndOfLine = 0.f;
147 lineLayout.height = 0.f;
148 lineLayout.ascender = 0.f;
150 // Stores temporary line layout which has not been added to the final line layout.
151 LineLayout tmpLineLayout;
152 tmpLineLayout.numberOfCharacters = 0u;
153 tmpLineLayout.numberOfGlyphs = 0u;
154 tmpLineLayout.length = 0.f;
155 tmpLineLayout.wsLengthEndOfLine = 0.f;
156 tmpLineLayout.height = 0.f;
157 tmpLineLayout.ascender = 0.f;
159 // Get the last glyph index.
160 const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
162 FontId lastFontId = 0u;
163 for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
164 glyphIndex < parameters.totalNumberOfGlyphs;
167 // Get the glyph info.
168 const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
170 // Get the character indices for the current glyph. The last character index is needed
171 // because there are glyphs formed by more than one character but their break info is
172 // given only for the last character.
173 const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
174 const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
175 const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
177 // Get the line break info for the current character.
178 const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
180 // Get the word break info for the current character.
181 const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
183 // Increase the number of characters.
184 tmpLineLayout.numberOfCharacters += charactersPerGlyph;
186 // Increase the number of glyphs.
187 tmpLineLayout.numberOfGlyphs++;
189 // Increase the accumulated length.
190 tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
192 // Check if the accumulated length fits in the width of the box.
193 if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
195 // Current word does not fit in the box's width.
199 if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
201 if( glyphIndex == lastGlyphIndex )
203 // Must break the line. Update the line layout and return.
204 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
205 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
206 lineLayout.length += tmpLineLayout.length;
207 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
209 if( tmpLineLayout.height > lineLayout.height )
211 lineLayout.height = tmpLineLayout.height;
214 if( tmpLineLayout.ascender > lineLayout.ascender )
216 lineLayout.ascender = tmpLineLayout.ascender;
220 tmpLineLayout.numberOfCharacters = 0u;
221 tmpLineLayout.numberOfGlyphs = 0u;
222 tmpLineLayout.length = 0u;
223 tmpLineLayout.wsLengthEndOfLine = 0u;
224 tmpLineLayout.height = 0.f;
225 tmpLineLayout.ascender = 0.f;
229 if( TextAbstraction::WORD_BREAK == wordBreakInfo )
231 // Current glyph is the last one of the current word.
232 // Add the temporal layout to the current one.
233 lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
234 lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
235 lineLayout.length += tmpLineLayout.length;
236 lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
238 if( tmpLineLayout.height > lineLayout.height )
240 lineLayout.height = tmpLineLayout.height;
243 if( tmpLineLayout.ascender > lineLayout.ascender )
245 lineLayout.ascender = tmpLineLayout.ascender;
248 tmpLineLayout.numberOfCharacters = 0u;
249 tmpLineLayout.numberOfGlyphs = 0u;
250 tmpLineLayout.length = 0u;
251 tmpLineLayout.wsLengthEndOfLine = 0u;
252 tmpLineLayout.height = 0.f;
253 tmpLineLayout.ascender = 0.f;
256 if( lastFontId != glyphInfo.fontId )
258 Text::FontMetrics fontMetrics;
259 mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
261 // Sets the maximum height.
262 if( fontMetrics.height > tmpLineLayout.height )
264 tmpLineLayout.height = fontMetrics.height;
267 // Sets the maximum ascender.
268 if( fontMetrics.ascender > tmpLineLayout.ascender )
270 tmpLineLayout.ascender = fontMetrics.ascender;
273 lastFontId = glyphInfo.fontId;
278 bool LayoutText( const LayoutParameters& layoutParameters,
279 Vector<Vector2>& glyphPositions,
282 // TODO Switch between different layouts
287 case LayoutEngine::SINGLE_LINE_BOX:
289 update = SingleLineLayout( layoutParameters,
294 case LayoutEngine::MULTI_LINE_BOX:
296 update = MultiLineLayout( layoutParameters,
308 // TODO - Rewrite this to handle bidi
309 bool SingleLineLayout( const LayoutParameters& layoutParameters,
310 Vector<Vector2>& glyphPositions,
314 layout.glyphIndex = 0u;
315 GetLineLayoutForBox( layoutParameters,
318 if( 0u == layout.numberOfGlyphs )
320 // The width is too small and no characters are laid-out.
324 // Update the actual size.
325 actualSize.width = layout.length;
326 actualSize.height = layout.height;
329 float penY = layout.height;
331 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
332 for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
334 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
335 Vector2& position = *( glyphPositionsBuffer + glyphIndex );
337 position.x = penX + glyph.xBearing;
338 position.y = penY - glyph.yBearing;
340 penX += glyph.advance;
346 // TODO - Rewrite this to handle bidi
347 bool MultiLineLayout( const LayoutParameters& layoutParameters,
348 Vector<Vector2>& glyphPositions,
352 for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
356 // Get the layout for the line.
358 layout.glyphIndex = index;
359 GetMultiLineLayoutForBox( layoutParameters,
362 if( 0u == layout.numberOfGlyphs )
364 // The width is too small and no characters are laid-out.
368 // Update the actual size.
369 if( layout.length > actualSize.width )
371 actualSize.width = layout.length;
374 actualSize.height += layout.height;
376 // Traverse the glyphs and set the positions.
378 penY += layout.height;
380 Vector2* glyphPositionsBuffer = glyphPositions.Begin();
381 for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
383 const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
384 Vector2& position = *( glyphPositionsBuffer + i );
386 position.x = penX + glyph.xBearing;
387 position.y = penY - glyph.yBearing;
389 penX += glyph.advance;
392 // Increase the glyph index.
393 index += layout.numberOfGlyphs;
399 LayoutEngine::Layout mLayout;
401 TextAbstraction::FontClient mFontClient;
404 LayoutEngine::LayoutEngine()
407 mImpl = new LayoutEngine::Impl();
410 LayoutEngine::~LayoutEngine()
415 void LayoutEngine::SetLayout( Layout layout )
417 mImpl->mLayout = layout;
420 unsigned int LayoutEngine::GetLayout() const
422 return mImpl->mLayout;
425 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
426 Vector<Vector2>& glyphPositions,
429 return mImpl->LayoutText( layoutParameters,
436 } // namespace Toolkit