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