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     const bool rtlDirection = IsRightToLeftScript( script );
131     hb_buffer_set_direction( harfBuzzBuffer,
132                              rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
133
134     hb_buffer_set_script( harfBuzzBuffer,
135                           SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
136
137     hb_buffer_set_language( harfBuzzBuffer,
138                             hb_language_from_string( DEFAULT_LANGUAGE,
139                                                      DEFAULT_LANGUAGE_LENGTH ) );
140
141     /* Layout the text */
142     hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
143
144     hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
145
146     /* Get glyph data */
147     unsigned int glyphCount;
148     hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
149     hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
150
151     const Length lastGlyphIndex = glyphCount - 1u;
152     for( Length i = 0u; i < glyphCount; ++i )
153     {
154       // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
155       // The glyphs are needed in the logical order to layout the text in lines.
156       const Length index = rtlDirection ? ( lastGlyphIndex - i ) : i;
157
158       mIndices.PushBack( glyphInfo[index].codepoint );
159       mAdvance.PushBack( glyphPositions[index].x_advance / TO_PIXELS );
160       mCharacterMap.PushBack( glyphInfo[index].cluster );
161     }
162
163     /* Cleanup */
164     hb_buffer_destroy( harfBuzzBuffer );
165     hb_font_destroy( harfBuzzFont );
166     FT_Done_Face( face );
167
168     return mIndices.Count();
169   }
170
171   void GetGlyphs( GlyphInfo* glyphInfo,
172                   CharacterIndex* glyphToCharacterMap )
173   {
174     Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
175     Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
176     Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
177
178     for( Length index = 0u, size = mIndices.Count(); index < size; ++index )
179     {
180       GlyphInfo& glyph = *( glyphInfo + index );
181       CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
182
183       glyph.fontId = mFontId;
184       glyph.index = *( indicesIt + index );
185       glyph.advance = *( advanceIt + index );
186
187       glyphToCharacter = *( characterMapIt + index );
188     }
189   }
190
191   FT_Library             mFreeTypeLibrary;
192
193   Vector<CharacterIndex> mIndices;
194   Vector<float>          mAdvance;
195   Vector<CharacterIndex> mCharacterMap;
196   FontId                 mFontId;
197 };
198
199 Shaping::Shaping()
200 : mPlugin( NULL )
201 {
202 }
203
204 Shaping::~Shaping()
205 {
206   delete mPlugin;
207 }
208
209 TextAbstraction::Shaping Shaping::Get()
210 {
211   TextAbstraction::Shaping shapingHandle;
212
213   SingletonService service( SingletonService::Get() );
214   if( service )
215   {
216     // Check whether the singleton is already created
217     Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
218     if( handle )
219     {
220       // If so, downcast the handle
221       Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
222       shapingHandle = TextAbstraction::Shaping( impl );
223     }
224     else // create and register the object
225     {
226       shapingHandle = TextAbstraction::Shaping( new Shaping );
227       service.Register( typeid( shapingHandle ), shapingHandle );
228     }
229   }
230
231   return shapingHandle;
232 }
233
234 Length Shaping::Shape( const Character* const text,
235                        Length numberOfCharacters,
236                        FontId fontId,
237                        Script script )
238 {
239   CreatePlugin();
240
241   return mPlugin->Shape( text,
242                          numberOfCharacters,
243                          fontId,
244                          script );
245 }
246
247 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
248                          CharacterIndex* glyphToCharacterMap )
249 {
250   CreatePlugin();
251
252   mPlugin->GetGlyphs( glyphInfo,
253                       glyphToCharacterMap );
254 }
255
256 void Shaping::CreatePlugin()
257 {
258   if( !mPlugin )
259   {
260     mPlugin = new Plugin();
261     mPlugin->Initialize();
262   }
263 }
264
265 } // namespace Internal
266
267 } // namespace TextAbstraction
268
269 } // namespace Dali