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