Merge remote-tracking branch 'origin/tizen' into new_text
[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/public-api/text-abstraction/font-client.h>
24 #include <dali/public-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 unsigned int HIGH_QUALITY_PIXEL_SIZE = 200u;  // Pixel size sent to FreeType2 FT_Set_Char_Size() for high quality glyphs.
43 const char*        DEFAULT_LANGUAGE = "en";
44 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
45 const float        TO_PIXELS = 64.f;
46
47 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
48 {
49   HB_SCRIPT_LATIN,
50   HB_SCRIPT_ARABIC,
51   HB_SCRIPT_DEVANAGARI,
52   HB_SCRIPT_BENGALI,
53   HB_SCRIPT_GURMUKHI,
54   HB_SCRIPT_GUJARATI,
55   HB_SCRIPT_ORIYA,
56   HB_SCRIPT_TAMIL,
57   HB_SCRIPT_TELUGU,
58   HB_SCRIPT_KANNADA,
59   HB_SCRIPT_MALAYALAM,
60   HB_SCRIPT_SINHALA,
61   HB_SCRIPT_HAN,
62   HB_SCRIPT_HANGUL,
63   HB_SCRIPT_KHMER,
64   HB_SCRIPT_LAO,
65   HB_SCRIPT_THAI,
66   HB_SCRIPT_MYANMAR,
67   HB_SCRIPT_UNKNOWN
68 };
69
70 struct Shaping::Plugin
71 {
72   Plugin()
73   : mFreeTypeLibrary( NULL ),
74     mIndices(),
75     mAdvance(),
76     mCharacterMap(),
77     mFontId( 0u )
78   {
79   }
80
81   ~Plugin()
82   {
83     FT_Done_FreeType( mFreeTypeLibrary );
84   }
85
86   void Initialize()
87   {
88     int error = FT_Init_FreeType( &mFreeTypeLibrary );
89     if( FT_Err_Ok != error )
90     {
91       DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
92     }
93   }
94
95   Length Shape( const Character* const text,
96                 Length numberOfCharacters,
97                 FontId fontId,
98                 Script script )
99   {
100     // Clear previoursly shaped texts.
101     mIndices.Clear();
102     mAdvance.Clear();
103     mCharacterMap.Clear();
104     mFontId = fontId;
105
106     TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
107
108     // Get the font's path file name from the font Id.
109     FontDescription fontDescription;
110     fontClient.GetDescription( fontId, fontDescription );
111
112     // Create a FreeType font's face.
113     FT_Face face;
114     FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
115     if( FT_Err_Ok != retVal )
116     {
117       DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
118       return 0u;
119     }
120
121     FT_Set_Pixel_Sizes( face, HIGH_QUALITY_PIXEL_SIZE, HIGH_QUALITY_PIXEL_SIZE );
122
123     /* Get our harfbuzz font struct */
124     hb_font_t* harfBuzzFont;
125     harfBuzzFont = hb_ft_font_create( face, NULL );
126
127     /* Create a buffer for harfbuzz to use */
128     hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
129
130     hb_buffer_set_direction( harfBuzzBuffer,
131                              ( ARABIC == script ) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
132
133     hb_buffer_set_script( harfBuzzBuffer,
134                           SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
135
136     hb_buffer_set_language( harfBuzzBuffer,
137                             hb_language_from_string( DEFAULT_LANGUAGE,
138                                                      DEFAULT_LANGUAGE_LENGTH ) );
139
140     /* Layout the text */
141     hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
142
143     hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
144
145     /* Get glyph data */
146     unsigned int glyphCount;
147     hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
148     hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
149
150     for( Length i = 0u; i < glyphCount; ++i )
151     {
152       mIndices.PushBack( glyphInfo[i].codepoint );
153       mAdvance.PushBack( glyphPositions[i].x_advance / TO_PIXELS );
154       mCharacterMap.PushBack( glyphInfo[i].cluster );
155     }
156
157     /* Cleanup */
158     hb_buffer_destroy( harfBuzzBuffer );
159     hb_font_destroy( harfBuzzFont );
160     FT_Done_Face( face );
161
162     return mIndices.Count();
163   }
164
165   void GetGlyphs( GlyphInfo* glyphInfo,
166                   CharacterIndex* glyphToCharacterMap )
167   {
168     Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
169     Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
170     Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
171
172     for( Length index = 0u, size = mIndices.Count(); index < size; ++index )
173     {
174       GlyphInfo& glyph = *( glyphInfo + index );
175       CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
176
177       glyph.fontId = mFontId;
178       glyph.index = *( indicesIt + index );
179       glyph.advance = *( advanceIt + index );
180
181       glyphToCharacter = *( characterMapIt + index );
182     }
183   }
184
185   FT_Library             mFreeTypeLibrary;
186
187   Vector<CharacterIndex> mIndices;
188   Vector<float>          mAdvance;
189   Vector<CharacterIndex> mCharacterMap;
190   FontId                 mFontId;
191 };
192
193 Shaping::Shaping()
194 : mPlugin( NULL )
195 {
196 }
197
198 Shaping::~Shaping()
199 {
200   delete mPlugin;
201 }
202
203 TextAbstraction::Shaping Shaping::Get()
204 {
205   TextAbstraction::Shaping shapingHandle;
206
207   SingletonService service( SingletonService::Get() );
208   if( service )
209   {
210     // Check whether the singleton is already created
211     Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
212     if( handle )
213     {
214       // If so, downcast the handle
215       Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
216       shapingHandle = TextAbstraction::Shaping( impl );
217     }
218     else // create and register the object
219     {
220       shapingHandle = TextAbstraction::Shaping( new Shaping );
221       service.Register( typeid( shapingHandle ), shapingHandle );
222     }
223   }
224
225   return shapingHandle;
226 }
227
228 Length Shaping::Shape( const Character* const text,
229                        Length numberOfCharacters,
230                        FontId fontId,
231                        Script script )
232 {
233   CreatePlugin();
234
235   return mPlugin->Shape( text,
236                          numberOfCharacters,
237                          fontId,
238                          script );
239 }
240
241 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
242                          CharacterIndex* glyphToCharacterMap )
243 {
244   CreatePlugin();
245
246   mPlugin->GetGlyphs( glyphInfo,
247                       glyphToCharacterMap );
248 }
249
250 void Shaping::CreatePlugin()
251 {
252   if( !mPlugin )
253   {
254     mPlugin = new Plugin();
255     mPlugin->Initialize();
256   }
257 }
258
259 } // namespace Internal
260
261 } // namespace TextAbstraction
262
263 } // namespace Dali