e6a3a63f35066b863fdb48a71cfce38d27a5e94f
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/public-api/text/layouts/layout-engine.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/public-api/text/logical-model.h>
27 #include <dali-toolkit/public-api/text/visual-model.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Text
36 {
37
38 struct LayoutEngine::Impl
39 {
40   Impl()
41   : mLayout( LayoutEngine::SINGLE_LINE_BOX )
42   {
43     mFontClient = TextAbstraction::FontClient::Get();
44   }
45
46   void UpdateVisualModel( const Vector2& boundingBox,
47                           const Vector<GlyphInfo>& glyphs,
48                           const Vector<CharacterIndex>& characterIndices,
49                           const Vector<Length>& charactersPerGlyph,
50                           VisualModel& visualModel )
51   {
52     // TODO Switch between different layouts
53
54     visualModel.SetGlyphs( &glyphs[0],
55                            &characterIndices[0],
56                            &charactersPerGlyph[0],
57                            glyphs.Count() );
58
59     UpdateGlyphPositions( boundingBox, visualModel );
60   }
61
62   void UpdateGlyphPositions( const Vector2& boundingBox, VisualModel& visualModel )
63   {
64     if( LayoutEngine::SINGLE_LINE_BOX == mLayout )
65     {
66       SingleLineLayout( boundingBox, visualModel );
67     }
68     else
69     {
70       MultiLineLayout( boundingBox, visualModel );
71     }
72   }
73
74   // TODO - Rewrite this to handle bidi
75   void SingleLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
76   {
77     Length glyphCount = visualModel.GetNumberOfGlyphs();
78
79     std::vector<Vector2> glyphPositions;
80     glyphPositions.reserve( glyphCount );
81
82     if( glyphCount > 0 )
83     {
84       // FIXME Single font assumption
85       Text::FontMetrics fontMetrics;
86       GlyphInfo firstGlyph;
87       visualModel.GetGlyphs( &firstGlyph, 0, 1 );
88       mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
89
90       float penX( 0 );
91       float penY( fontMetrics.ascender ); // Move to baseline
92
93       for( unsigned int i=0; i<glyphCount; ++i )
94       {
95         GlyphInfo glyph;
96         visualModel.GetGlyphs( &glyph, i, 1 );
97
98         glyphPositions.push_back( Vector2( penX + glyph.xBearing,
99                                            penY - glyph.yBearing ) );
100
101         penX += glyph.advance;
102       }
103
104       visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
105
106       visualModel.SetActualSize( Vector2(penX, fontMetrics.height) );
107     }
108   }
109
110   // TODO - Rewrite this to handle bidi
111   void MultiLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
112   {
113     Length glyphCount = visualModel.GetNumberOfGlyphs();
114
115     std::vector<Vector2> glyphPositions;
116     glyphPositions.reserve( glyphCount );
117
118     if( glyphCount > 0 )
119     {
120       Size actualSize;
121
122       // FIXME Single font assumption
123       Text::FontMetrics fontMetrics;
124       GlyphInfo firstGlyph;
125       visualModel.GetGlyphs( &firstGlyph, 0, 1 );
126       mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
127
128       float penX( 0 );
129       float penY( fontMetrics.ascender ); // Move to baseline
130
131       unsigned int i=0;
132       while( i < glyphCount )
133       {
134         // Skip initial whitespace
135         for( ; i<glyphCount; ++i )
136         {
137           GlyphInfo glyph;
138           visualModel.GetGlyphs( &glyph, i, 1 );
139
140           if( glyph.width  > 0 &&
141               glyph.height > 0 )
142           {
143             break;
144           }
145           else
146           {
147             glyphPositions.push_back( Vector2( penX + glyph.xBearing,
148                                                penY - glyph.yBearing ) );
149           }
150         }
151
152         // Find last glyph for the next line
153         unsigned int endIndex = i;
154         float endPenX = penX;
155         unsigned int j=i;
156         for( ; j<glyphCount; ++j )
157         {
158           GlyphInfo glyph;
159           visualModel.GetGlyphs( &glyph, j, 1 );
160
161           endPenX += glyph.advance;
162
163           if( glyph.width  <= 0 ||
164               glyph.height <= 0 )
165           {
166             // Potential line end found
167             endIndex = j;
168           }
169           else if( endPenX > boundingBox.width )
170           {
171             break;
172           }
173         }
174
175         actualSize.width = ( actualSize.width < endPenX ) ? endPenX : actualSize.width;
176
177         // If end of text or no whitespace found
178         if( glyphCount == j ||
179             endIndex == i )
180         {
181           endIndex = j;
182         }
183
184         for( ; i<endIndex; ++i )
185         {
186           GlyphInfo glyph;
187           visualModel.GetGlyphs( &glyph, i, 1 );
188
189           glyphPositions.push_back( Vector2( penX + glyph.xBearing,
190                                              penY - glyph.yBearing ) );
191
192           penX += glyph.advance;
193         }
194
195         // Go to next line
196         penX = 0;
197         penY += fontMetrics.height;
198
199         actualSize.height += fontMetrics.height;
200       }
201
202       visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
203
204       visualModel.SetActualSize( actualSize );
205     }
206   }
207
208   unsigned int mLayout;
209
210   TextAbstraction::FontClient mFontClient;
211 };
212
213 LayoutEngine::LayoutEngine()
214 : mImpl( NULL )
215 {
216   mImpl = new LayoutEngine::Impl();
217 }
218
219 LayoutEngine::~LayoutEngine()
220 {
221   delete mImpl;
222 }
223
224 void LayoutEngine::SetLayout( Layout layout )
225 {
226   mImpl->mLayout = layout;
227 }
228
229 unsigned int LayoutEngine::GetLayout() const
230 {
231   return mImpl->mLayout;
232 }
233
234 void LayoutEngine::UpdateVisualModel( const Vector2& boundingBox,
235                                       const Vector<GlyphInfo>& glyphs,
236                                       const Vector<CharacterIndex>& characterIndices,
237                                       const Vector<Length>& charactersPerGlyph,
238                                       VisualModel& visualModel )
239 {
240   mImpl->UpdateVisualModel( boundingBox,
241                             glyphs,
242                             characterIndices,
243                             charactersPerGlyph,
244                             visualModel );
245 }
246
247 } // namespace Text
248
249 } // namespace Toolkit
250
251 } // namespace Dali