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