Merge branch 'tizen' into devel/new_mesh
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / shaping-impl.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 "shaping-impl.h"
20
21 // INTERNAL INCLUDES
22 #include <singleton-service-impl.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/devel-api/text-abstraction/glyph-info.h>
25 #include <dali/integration-api/debug.h>
26
27 // EXTERNAL INCLUDES
28 #include <harfbuzz/hb.h>
29 #include <harfbuzz/hb-ft.h>
30
31 #include <ft2build.h>
32
33 namespace Dali
34 {
35
36 namespace TextAbstraction
37 {
38
39 namespace Internal
40 {
41
42 const char*        DEFAULT_LANGUAGE = "en";
43 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
44 const float        FROM_266 = 1.0f / 64.0f;
45
46 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
47 {
48   HB_SCRIPT_CYRILLIC,
49   HB_SCRIPT_GREEK,
50   HB_SCRIPT_LATIN,
51
52   HB_SCRIPT_ARABIC,
53   HB_SCRIPT_HEBREW,
54
55   HB_SCRIPT_ARMENIAN,
56   HB_SCRIPT_GEORGIAN,
57
58   HB_SCRIPT_HAN,
59   HB_SCRIPT_HANGUL,
60   HB_SCRIPT_HIRAGANA,
61   HB_SCRIPT_KATAKANA,
62
63   HB_SCRIPT_BENGALI,
64   HB_SCRIPT_MYANMAR,
65   HB_SCRIPT_DEVANAGARI,
66   HB_SCRIPT_GUJARATI,
67   HB_SCRIPT_GURMUKHI,
68   HB_SCRIPT_KANNADA,
69   HB_SCRIPT_MALAYALAM,
70   HB_SCRIPT_ORIYA,
71   HB_SCRIPT_SINHALA,
72   HB_SCRIPT_TAMIL,
73   HB_SCRIPT_TELUGU,
74
75   HB_SCRIPT_LAO,
76   HB_SCRIPT_THAI,
77   HB_SCRIPT_KHMER,
78
79   HB_SCRIPT_UNKNOWN, // EMOJI
80   HB_SCRIPT_UNKNOWN
81 };
82
83 struct Shaping::Plugin
84 {
85   Plugin()
86   : mFreeTypeLibrary( NULL ),
87     mIndices(),
88     mAdvance(),
89     mCharacterMap(),
90     mFontId( 0u )
91   {
92   }
93
94   ~Plugin()
95   {
96     FT_Done_FreeType( mFreeTypeLibrary );
97   }
98
99   void Initialize()
100   {
101     int error = FT_Init_FreeType( &mFreeTypeLibrary );
102     if( FT_Err_Ok != error )
103     {
104       DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
105     }
106   }
107
108   Length Shape( const Character* const text,
109                 Length numberOfCharacters,
110                 FontId fontId,
111                 Script script )
112   {
113     // Clear previoursly shaped texts.
114     mIndices.Clear();
115     mAdvance.Clear();
116     mCharacterMap.Clear();
117     mOffset.Clear();
118     mFontId = fontId;
119
120     // Reserve some space to avoid reallocations.
121     const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( numberOfCharacters ) );
122     mIndices.Reserve( numberOfGlyphs );
123     mAdvance.Reserve( numberOfGlyphs );
124     mCharacterMap.Reserve( numberOfGlyphs );
125     mOffset.Reserve( 2u * numberOfGlyphs );
126
127     TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
128
129     // Get the font's path file name from the font Id.
130     FontDescription fontDescription;
131     fontClient.GetDescription( fontId, fontDescription );
132
133     // Create a FreeType font's face.
134     FT_Face face;
135     FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
136     if( FT_Err_Ok != retVal )
137     {
138       DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
139       return 0u;
140     }
141
142     unsigned int horizontalDpi = 0u;
143     unsigned int verticalDpi = 0u;
144     fontClient.GetDpi( horizontalDpi, verticalDpi );
145
146     FT_Set_Char_Size( face,
147                       0u,
148                       fontClient.GetPointSize( fontId ),
149                       horizontalDpi,
150                       verticalDpi );
151
152     /* Get our harfbuzz font struct */
153     hb_font_t* harfBuzzFont;
154     harfBuzzFont = hb_ft_font_create( face, NULL );
155
156     /* Create a buffer for harfbuzz to use */
157     hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
158
159     const bool rtlDirection = IsRightToLeftScript( script );
160     hb_buffer_set_direction( harfBuzzBuffer,
161                              rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
162
163     hb_buffer_set_script( harfBuzzBuffer,
164                           SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
165
166     hb_buffer_set_language( harfBuzzBuffer,
167                             hb_language_from_string( DEFAULT_LANGUAGE,
168                                                      DEFAULT_LANGUAGE_LENGTH ) );
169
170     /* Layout the text */
171     hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
172
173     hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
174
175     /* Get glyph data */
176     unsigned int glyphCount;
177     hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
178     hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
179     const GlyphIndex lastGlyphIndex = glyphCount - 1u;
180     for( GlyphIndex i = 0u; i < glyphCount; )
181     {
182       if( rtlDirection )
183       {
184         // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
185         // The glyphs are needed in the logical order to layout the text in lines.
186         // Do not change the order of the glyphs if they belong to the same cluster.
187         GlyphIndex rtlIndex = lastGlyphIndex - i;
188
189         unsigned int cluster = glyphInfo[rtlIndex].cluster;
190         unsigned int previousCluster = cluster;
191         Length numberOfGlyphsInCluster = 0u;
192
193         while( ( cluster == previousCluster ) )
194         {
195           ++numberOfGlyphsInCluster;
196           previousCluster = cluster;
197
198           if( rtlIndex > 0u )
199           {
200             --rtlIndex;
201
202             cluster = glyphInfo[rtlIndex].cluster;
203           }
204           else
205           {
206             break;
207           }
208         }
209
210         rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) );
211
212         for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j )
213         {
214           const GlyphIndex index = rtlIndex + j;
215
216           mIndices.PushBack( glyphInfo[index].codepoint );
217           mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) );
218           mCharacterMap.PushBack( glyphInfo[index].cluster );
219           mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) );
220           mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) );
221         }
222
223         i += numberOfGlyphsInCluster;
224       }
225       else
226       {
227         mIndices.PushBack( glyphInfo[i].codepoint );
228         mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) );
229         mCharacterMap.PushBack( glyphInfo[i].cluster );
230         mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) );
231         mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) );
232
233         ++i;
234       }
235     }
236
237     /* Cleanup */
238     hb_buffer_destroy( harfBuzzBuffer );
239     hb_font_destroy( harfBuzzFont );
240     FT_Done_Face( face );
241
242     return mIndices.Count();
243   }
244
245   void GetGlyphs( GlyphInfo* glyphInfo,
246                   CharacterIndex* glyphToCharacterMap )
247   {
248     Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
249     Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
250     Vector<float>::ConstIterator offsetIt = mOffset.Begin();
251     Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
252
253     for( GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index )
254     {
255       GlyphInfo& glyph = *( glyphInfo + index );
256       CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
257
258       glyph.fontId = mFontId;
259       glyph.index = *( indicesIt + index );
260       glyph.advance = *( advanceIt + index );
261
262       const GlyphIndex offsetIndex = 2u * index;
263       glyph.xBearing = *( offsetIt + offsetIndex );
264       glyph.yBearing = *( offsetIt + offsetIndex + 1u );
265
266       glyphToCharacter = *( characterMapIt + index );
267     }
268   }
269
270   FT_Library             mFreeTypeLibrary;
271
272   Vector<CharacterIndex> mIndices;
273   Vector<float>          mAdvance;
274   Vector<float>          mOffset;
275   Vector<CharacterIndex> mCharacterMap;
276   FontId                 mFontId;
277 };
278
279 Shaping::Shaping()
280 : mPlugin( NULL )
281 {
282 }
283
284 Shaping::~Shaping()
285 {
286   delete mPlugin;
287 }
288
289 TextAbstraction::Shaping Shaping::Get()
290 {
291   TextAbstraction::Shaping shapingHandle;
292
293   SingletonService service( SingletonService::Get() );
294   if( service )
295   {
296     // Check whether the singleton is already created
297     Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
298     if( handle )
299     {
300       // If so, downcast the handle
301       Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
302       shapingHandle = TextAbstraction::Shaping( impl );
303     }
304     else // create and register the object
305     {
306       shapingHandle = TextAbstraction::Shaping( new Shaping );
307       service.Register( typeid( shapingHandle ), shapingHandle );
308     }
309   }
310
311   return shapingHandle;
312 }
313
314 Length Shaping::Shape( const Character* const text,
315                        Length numberOfCharacters,
316                        FontId fontId,
317                        Script script )
318 {
319   CreatePlugin();
320
321   return mPlugin->Shape( text,
322                          numberOfCharacters,
323                          fontId,
324                          script );
325 }
326
327 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
328                          CharacterIndex* glyphToCharacterMap )
329 {
330   CreatePlugin();
331
332   mPlugin->GetGlyphs( glyphInfo,
333                       glyphToCharacterMap );
334 }
335
336 void Shaping::CreatePlugin()
337 {
338   if( !mPlugin )
339   {
340     mPlugin = new Plugin();
341     mPlugin->Initialize();
342   }
343 }
344
345 } // namespace Internal
346
347 } // namespace TextAbstraction
348
349 } // namespace Dali