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