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