Merge remote-tracking branch 'origin/tizen' into new_text
[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, const LogicalModel& logicalModel, VisualModel& visualModel )
47   {
48     // TODO Switch between different layouts
49
50     TextAbstraction::FontId fontId = mFontClient.GetFontId( "/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-R.ttf", 13*64 );
51
52     const Length characterCount = logicalModel.GetNumberOfCharacters();
53
54     Vector<GlyphInfo> glyphs;
55     glyphs.Reserve( characterCount );
56
57     Vector<CharacterIndex> characterIndices;
58     characterIndices.Reserve( characterCount );
59
60     std::vector<Length> charactersPerGlyph;
61     charactersPerGlyph.assign( characterCount, 1 );
62
63     for( unsigned int i=0; i<characterCount; ++i )
64     {
65       Character charcode;
66       logicalModel.GetText( i, &charcode, 1 );
67
68       // TODO - Perform shaping to get correct glyph indices
69       GlyphIndex glyphIndex = mFontClient.GetGlyphIndex( fontId, charcode );
70
71       glyphs.PushBack( GlyphInfo(fontId, glyphIndex) );
72       characterIndices.PushBack( 1 );
73     }
74
75     if( mFontClient.GetGlyphMetrics( &glyphs[0], glyphs.Size() ) )
76     {
77       visualModel.SetGlyphs( &glyphs[0],
78                              &characterIndices[0],
79                              &charactersPerGlyph[0],
80                              characterCount );
81
82       UpdateGlyphPositions( boundingBox, visualModel );
83     }
84   }
85
86   void UpdateGlyphPositions( const Vector2& boundingBox, VisualModel& visualModel )
87   {
88     if( LayoutEngine::SINGLE_LINE_BOX == mLayout )
89     {
90       SingleLineLayout( boundingBox, visualModel );
91     }
92     else
93     {
94       MultiLineLayout( boundingBox, visualModel );
95     }
96   }
97
98   // TODO - Rewrite this to handle bidi
99   void SingleLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
100   {
101     Length glyphCount = visualModel.GetNumberOfGlyphs();
102
103     std::vector<Vector2> glyphPositions;
104     glyphPositions.reserve( glyphCount );
105
106     if( glyphCount > 0 )
107     {
108       // FIXME Single font assumption
109       Text::FontMetrics fontMetrics;
110       GlyphInfo firstGlyph;
111       visualModel.GetGlyphs( 0, &firstGlyph, 1 );
112       mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
113
114       float penX( 0 );
115       float penY( fontMetrics.ascender ); // Move to baseline
116
117       for( unsigned int i=0; i<glyphCount; ++i )
118       {
119         GlyphInfo glyph;
120         visualModel.GetGlyphs( i, &glyph, 1 );
121
122         glyphPositions.push_back( Vector2( penX + glyph.xBearing,
123                                            penY - glyph.yBearing ) );
124
125         penX += glyph.advance;
126       }
127
128       visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
129     }
130   }
131
132   // TODO - Rewrite this to handle bidi
133   void MultiLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
134   {
135     Length glyphCount = visualModel.GetNumberOfGlyphs();
136
137     std::vector<Vector2> glyphPositions;
138     glyphPositions.reserve( glyphCount );
139
140     if( glyphCount > 0 )
141     {
142       // FIXME Single font assumption
143       Text::FontMetrics fontMetrics;
144       GlyphInfo firstGlyph;
145       visualModel.GetGlyphs( 0, &firstGlyph, 1 );
146       mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
147
148       float penX( 0 );
149       float penY( fontMetrics.ascender ); // Move to baseline
150
151       unsigned int i=0;
152       while( i < glyphCount )
153       {
154         // Skip initial whitespace
155         for( ; i<glyphCount; ++i )
156         {
157           GlyphInfo glyph;
158           visualModel.GetGlyphs( i, &glyph, 1 );
159
160           if( glyph.width  > 0 &&
161               glyph.height > 0 )
162           {
163             break;
164           }
165           else
166           {
167             glyphPositions.push_back( Vector2( penX + glyph.xBearing,
168                                                penY - glyph.yBearing ) );
169           }
170         }
171
172         // Find last glyph for the next line
173         unsigned int endIndex = i;
174         float endPenX = penX;
175         unsigned int j=i;
176         for( ; j<glyphCount; ++j )
177         {
178           GlyphInfo glyph;
179           visualModel.GetGlyphs( j, &glyph, 1 );
180
181           endPenX += glyph.advance;
182
183           if( glyph.width  <= 0 ||
184               glyph.height <= 0 )
185           {
186             // Potential line end found
187             endIndex = j;
188           }
189           else if( endPenX > boundingBox.width )
190           {
191             break;
192           }
193         }
194
195         // If end of text or no whitespace found
196         if( glyphCount == j ||
197             endIndex == i )
198         {
199           endIndex = j;
200         }
201
202         for( ; i<endIndex; ++i )
203         {
204           GlyphInfo glyph;
205           visualModel.GetGlyphs( i, &glyph, 1 );
206
207           glyphPositions.push_back( Vector2( penX + glyph.xBearing,
208                                              penY - glyph.yBearing ) );
209
210           penX += glyph.advance;
211         }
212
213         // Go to next line
214         penX = 0;
215         penY += fontMetrics.height;
216       }
217
218       visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
219     }
220   }
221
222   unsigned int mLayout;
223
224   TextAbstraction::FontClient mFontClient;
225 };
226
227 LayoutEngine::LayoutEngine()
228 : mImpl( NULL )
229 {
230   mImpl = new LayoutEngine::Impl();
231 }
232
233 LayoutEngine::~LayoutEngine()
234 {
235   delete mImpl;
236 }
237
238 void LayoutEngine::SetLayout( Layout layout )
239 {
240   mImpl->mLayout = layout;
241 }
242
243 void LayoutEngine::UpdateVisualModel( const Vector2& boundingBox, const LogicalModel& logicalModel, VisualModel& visualModel )
244 {
245   mImpl->UpdateVisualModel( boundingBox, logicalModel, visualModel );
246 }
247
248 } // namespace Text
249
250 } // namespace Toolkit
251
252 } // namespace Dali