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