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