a9fb2f24c7f7a9890fb9557b890ca089fcb95b12
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-impl.cpp
1 /*
2  * Copyright (c) 2022 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/font-client-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <condition_variable>
23 #include <mutex>
24 #include <thread>
25 #if defined(VCONF_ENABLED)
26 #include <vconf.h>
27 #endif
28
29 // INTERNAL INCLUDES
30 #include <dali/devel-api/adaptor-framework/thread-settings.h>
31 #include <dali/devel-api/common/singleton-service.h>
32 #include <dali/internal/system/common/logging.h>
33 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
34 #include <dali/internal/window-system/common/window-system.h>
35
36 #include <dali/devel-api/text-abstraction/glyph-info.h>
37
38 // Use this macro only if need to log messages before the log function is set.
39 #define FONT_LOG_MESSAGE(level, format, ...)                                    \
40   do                                                                            \
41   {                                                                             \
42     char buffer[256];                                                           \
43     int  result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
44     if(result >= static_cast<int>(sizeof(buffer)))                              \
45     {                                                                           \
46       std::string log("Font log message is too long to fit in the buffer.\n");  \
47       Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log);      \
48       break;                                                                    \
49     }                                                                           \
50     std::string log(buffer);                                                    \
51     Dali::TizenPlatform::LogMessage(level, log);                                \
52   } while(0)
53
54 namespace Dali
55 {
56 namespace TextAbstraction
57 {
58 namespace Internal
59 {
60 Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
61 std::thread                       gPreCacheThread;
62 std::thread                       gPreLoadThread;
63 std::mutex                        gMutex;
64 std::condition_variable           gPreCacheCond;
65 std::condition_variable           gPreLoadCond;
66 bool                              gPreCacheThreadReady;
67 bool                              gPreLoadThreadReady;
68
69 /* TODO: This is to prevent duplicate calls of font pre-cache.
70  * We may support this later, but currently we can't guarantee the behaviour
71  * if there is a pre-cache call from the user after the font client has been created. */
72 bool gFontPreCacheAvailable = true;
73 bool gFontPreLoadAvailable  = true;
74
75 FontClient::FontClient()
76 : mPlugin(nullptr),
77   mDpiHorizontal(0),
78   mDpiVertical(0)
79 {
80 }
81
82 FontClient::~FontClient()
83 {
84   delete mPlugin;
85 }
86
87 Dali::TextAbstraction::FontClient FontClient::Get()
88 {
89   Dali::TextAbstraction::FontClient fontClientHandle;
90
91   Dali::SingletonService service(SingletonService::Get());
92   if(service)
93   {
94     // Check whether the singleton is already created
95     Dali::BaseHandle handle = service.GetSingleton(typeid(Dali::TextAbstraction::FontClient));
96     if(handle)
97     {
98       // If so, downcast the handle
99       FontClient* impl = dynamic_cast<Dali::TextAbstraction::Internal::FontClient*>(handle.GetObjectPtr());
100       fontClientHandle = Dali::TextAbstraction::FontClient(impl);
101     }
102     else // create and register the object
103     {
104       if(gPreCacheThread.joinable())
105       {
106         gPreCacheThread.join();
107         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n");
108       }
109
110       if(gPreLoadThread.joinable())
111       {
112         gPreLoadThread.join();
113         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreLoad thread join\n");
114       }
115
116       if(gPreCreatedFontClient)
117       {
118         fontClientHandle = gPreCreatedFontClient;
119         gPreCreatedFontClient.Reset(); // No longer needed
120       }
121       else
122       {
123         fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient);
124       }
125
126       fontClientHandle.InitDefaultFontDescription();
127
128       gFontPreCacheAvailable = false;
129       gFontPreLoadAvailable  = false;
130
131       uint32_t horizontalDpi, verticalDpi;
132       fontClientHandle.GetDpi(horizontalDpi, verticalDpi);
133       if(horizontalDpi == 0u || verticalDpi == 0u)
134       {
135         horizontalDpi = verticalDpi = 0u;
136         Dali::Internal::Adaptor::WindowSystem::GetDpi(horizontalDpi, verticalDpi);
137         fontClientHandle.SetDpi(horizontalDpi, verticalDpi);
138       }
139
140       service.Register(typeid(fontClientHandle), fontClientHandle);
141     }
142   }
143
144   return fontClientHandle;
145 }
146
147 Dali::TextAbstraction::FontClient FontClient::PreInitialize()
148 {
149   // Pre-cached font client already exists or pre-cache thread already running.
150   // Font client pre-cache includes caching of the default font description.
151   if(gPreCreatedFontClient && !gFontPreCacheAvailable)
152   {
153     return gPreCreatedFontClient;
154   }
155
156   if(!gPreCreatedFontClient)
157   {
158     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
159   }
160
161   gPreCreatedFontClient.InitDefaultFontDescription();
162
163   return gPreCreatedFontClient;
164 }
165
166 void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool syncCreation)
167 {
168   SetThreadName("FontThread-fc");
169
170   if(syncCreation)
171   {
172     std::unique_lock<std::mutex> lock(gMutex);
173     gPreCacheThreadReady = true;
174     gPreCacheCond.notify_one();
175     lock.unlock();
176   }
177
178   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
179   GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
180   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
181 }
182
183 void FontClient::PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool syncCreation)
184 {
185   SetThreadName("FontThread-ft");
186
187   if(syncCreation)
188   {
189     std::unique_lock<std::mutex> lock(gMutex);
190     gPreLoadThreadReady = true;
191     gPreLoadCond.notify_one();
192     lock.unlock();
193   }
194
195   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_RUN\n");
196   GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
197   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_RUN\n");
198 }
199
200 void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread, bool syncCreation)
201 {
202   if(!gFontPreCacheAvailable)
203   {
204     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
205     return;
206   }
207
208   if(gPreCacheThread.joinable())
209   {
210     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n");
211   }
212
213   if(!gPreCreatedFontClient)
214   {
215     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
216   }
217
218   gFontPreCacheAvailable = false;
219
220   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
221   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache extraFamilyList    : %zu\n", extraFamilyList.size());
222   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache localeFamily       : %s\n", localeFamily.c_str());
223   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache useThread          : %d\n", useThread);
224
225   if(useThread)
226   {
227     if(syncCreation)
228     {
229       // The main thread wakes up upon receiving a notification from the pre-cache thread.
230       // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
231       const std::chrono::milliseconds timeout(1000);
232       gPreCacheThreadReady = false;
233       std::unique_lock<std::mutex> lock(gMutex);
234       FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
235       gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
236       gPreCacheCond.wait_for(lock, timeout, []{return gPreCacheThreadReady;});
237       FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
238     }
239     else
240     {
241       gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
242     }
243   }
244   else
245   {
246     GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
247   }
248 }
249
250 void FontClient::PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread, bool syncCreation)
251 {
252   if(!gFontPreLoadAvailable)
253   {
254     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load run failed, as a pre-loaded font client already exists.\n");
255     return;
256   }
257
258   if(gPreLoadThread.joinable())
259   {
260     FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load thread already running.\n");
261     return;
262   }
263
264   if(!gPreCreatedFontClient)
265   {
266     gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
267   }
268
269   gFontPreLoadAvailable = false;
270
271   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad fontPathList       : %zu\n", fontPathList.size());
272   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad memoryFontPathList : %zu\n", memoryFontPathList.size());
273   FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad useThread          : %d\n", useThread);
274
275   if(useThread)
276   {
277     if(syncCreation)
278     {
279       // The main thread wakes up upon receiving a notification from the pre-load thread.
280       // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
281       const std::chrono::milliseconds timeout(1000);
282       gPreLoadThreadReady = false;
283       std::unique_lock<std::mutex> lock(gMutex);
284       FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
285       gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
286       gPreLoadCond.wait_for(lock, timeout, []{return gPreLoadThreadReady;});
287       FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
288     }
289     else
290     {
291       gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
292     }
293   }
294   else
295   {
296     GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
297   }
298 }
299
300 void FontClient::ClearCache()
301 {
302   if(mPlugin)
303   {
304     mPlugin->ClearCache();
305   }
306 }
307
308 void FontClient::SetDpi(unsigned int horizontalDpi, unsigned int verticalDpi)
309 {
310   mDpiHorizontal = horizontalDpi;
311   mDpiVertical   = verticalDpi;
312
313   // Allow DPI to be set without loading plugin
314   if(mPlugin)
315   {
316     mPlugin->SetDpi(horizontalDpi, verticalDpi);
317   }
318 }
319
320 void FontClient::GetDpi(unsigned int& horizontalDpi, unsigned int& verticalDpi)
321 {
322   horizontalDpi = mDpiHorizontal;
323   verticalDpi   = mDpiVertical;
324 }
325
326 int FontClient::GetDefaultFontSize()
327 {
328   int fontSize(-1);
329
330 #if defined(VCONF_ENABLED)
331   vconf_get_int(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, &fontSize);
332 #endif
333
334   return fontSize;
335 }
336
337 void FontClient::ResetSystemDefaults()
338 {
339   CreatePlugin();
340
341   mPlugin->ResetSystemDefaults();
342 }
343
344 void FontClient::GetDefaultFonts(FontList& defaultFonts)
345 {
346   CreatePlugin();
347
348   mPlugin->GetDefaultFonts(defaultFonts);
349 }
350
351 void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
352 {
353   CreatePlugin();
354
355   mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
356 }
357
358 void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
359 {
360   CreatePlugin();
361
362   mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
363 }
364
365 void FontClient::InitDefaultFontDescription()
366 {
367   CreatePlugin();
368
369   mPlugin->InitDefaultFontDescription();
370 }
371
372 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
373 {
374   CreatePlugin();
375
376   mPlugin->GetDefaultPlatformFontDescription(fontDescription);
377 }
378
379 void FontClient::GetDescription(FontId fontId, FontDescription& fontDescription)
380 {
381   CreatePlugin();
382
383   mPlugin->GetDescription(fontId, fontDescription);
384 }
385
386 PointSize26Dot6 FontClient::GetPointSize(FontId fontId)
387 {
388   CreatePlugin();
389
390   return mPlugin->GetPointSize(fontId);
391 }
392
393 bool FontClient::IsCharacterSupportedByFont(FontId fontId, Character character)
394 {
395   CreatePlugin();
396
397   return mPlugin->IsCharacterSupportedByFont(fontId, character);
398 }
399
400 void FontClient::GetSystemFonts(FontList& systemFonts)
401 {
402   CreatePlugin();
403
404   mPlugin->GetSystemFonts(systemFonts);
405 }
406
407 FontId FontClient::FindDefaultFont(Character       charcode,
408                                    PointSize26Dot6 requestedPointSize,
409                                    bool            preferColor)
410 {
411   CreatePlugin();
412
413   return mPlugin->FindDefaultFont(charcode,
414                                   requestedPointSize,
415                                   preferColor);
416 }
417
418 FontId FontClient::FindFallbackFont(Character              charcode,
419                                     const FontDescription& preferredFontDescription,
420                                     PointSize26Dot6        requestedPointSize,
421                                     bool                   preferColor)
422 {
423   CreatePlugin();
424
425   return mPlugin->FindFallbackFont(charcode,
426                                    preferredFontDescription,
427                                    requestedPointSize,
428                                    preferColor);
429 }
430
431 bool FontClient::IsScalable(const FontPath& path)
432 {
433   CreatePlugin();
434
435   return mPlugin->IsScalable(path);
436 }
437
438 bool FontClient::IsScalable(const FontDescription& fontDescription)
439 {
440   CreatePlugin();
441
442   return mPlugin->IsScalable(fontDescription);
443 }
444
445 void FontClient::GetFixedSizes(const FontPath& path, Dali::Vector<PointSize26Dot6>& sizes)
446 {
447   CreatePlugin();
448
449   mPlugin->GetFixedSizes(path, sizes);
450 }
451
452 void FontClient::GetFixedSizes(const FontDescription&         fontDescription,
453                                Dali::Vector<PointSize26Dot6>& sizes)
454 {
455   CreatePlugin();
456
457   mPlugin->GetFixedSizes(fontDescription, sizes);
458 }
459
460 bool FontClient::HasItalicStyle(FontId fontId) const
461 {
462   if(!mPlugin)
463   {
464     return false;
465   }
466   return mPlugin->HasItalicStyle(fontId);
467 }
468
469 FontId FontClient::GetFontId(const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex)
470 {
471   CreatePlugin();
472
473   return mPlugin->GetFontIdByPath(path,
474                                   requestedPointSize,
475                                   faceIndex,
476                                   true);
477 }
478
479 FontId FontClient::GetFontId(const FontDescription& fontDescription,
480                              PointSize26Dot6        requestedPointSize,
481                              FaceIndex              faceIndex)
482 {
483   CreatePlugin();
484
485   return mPlugin->GetFontId(fontDescription,
486                             requestedPointSize,
487                             faceIndex);
488 }
489
490 FontId FontClient::GetFontId(const BitmapFont& bitmapFont)
491 {
492   CreatePlugin();
493
494   return mPlugin->GetFontId(bitmapFont);
495 }
496
497 void FontClient::GetFontMetrics(FontId fontId, FontMetrics& metrics)
498 {
499   CreatePlugin();
500
501   mPlugin->GetFontMetrics(fontId, metrics);
502 }
503
504 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode)
505 {
506   CreatePlugin();
507
508   return mPlugin->GetGlyphIndex(fontId, charcode);
509 }
510
511 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode, Character variantSelector)
512 {
513   CreatePlugin();
514
515   return mPlugin->GetGlyphIndex(fontId, charcode, variantSelector);
516 }
517
518 bool FontClient::GetGlyphMetrics(GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal)
519 {
520   CreatePlugin();
521
522   return mPlugin->GetGlyphMetrics(array, size, type, horizontal);
523 }
524
525 void FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth)
526 {
527   CreatePlugin();
528
529   mPlugin->CreateBitmap(fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth);
530 }
531
532 PixelData FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
533 {
534   CreatePlugin();
535
536   return mPlugin->CreateBitmap(fontId, glyphIndex, outlineWidth);
537 }
538
539 void FontClient::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
540 {
541   CreatePlugin();
542
543   mPlugin->CreateVectorBlob(fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
544 }
545
546 const GlyphInfo& FontClient::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
547 {
548   CreatePlugin();
549
550   return mPlugin->GetEllipsisGlyph(requestedPointSize);
551 }
552
553 bool FontClient::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
554 {
555   CreatePlugin();
556
557   return mPlugin->IsColorGlyph(fontId, glyphIndex);
558 }
559
560 GlyphIndex FontClient::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
561 {
562   CreatePlugin();
563
564   return mPlugin->CreateEmbeddedItem(description, pixelFormat);
565 }
566
567 void FontClient::EnableAtlasLimitation(bool enabled)
568 {
569   CreatePlugin();
570   return mPlugin->EnableAtlasLimitation(enabled);
571 }
572
573 bool FontClient::IsAtlasLimitationEnabled() const
574 {
575   if(mPlugin)
576   {
577     return mPlugin->IsAtlasLimitationEnabled();
578   }
579   return TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED;
580 }
581
582 Size FontClient::GetMaximumTextAtlasSize() const
583 {
584   if(mPlugin)
585   {
586     return mPlugin->GetMaximumTextAtlasSize();
587   }
588   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
589 }
590
591 Size FontClient::GetDefaultTextAtlasSize() const
592 {
593   if(mPlugin)
594   {
595     return mPlugin->GetDefaultTextAtlasSize();
596   }
597   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
598 }
599
600 Size FontClient::GetCurrentMaximumBlockSizeFitInAtlas() const
601 {
602   if(mPlugin)
603   {
604     return mPlugin->GetCurrentMaximumBlockSizeFitInAtlas();
605   }
606   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
607 }
608
609 bool FontClient::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
610 {
611   CreatePlugin();
612   return mPlugin->SetCurrentMaximumBlockSizeFitInAtlas(currentMaximumBlockSizeFitInAtlas);
613 }
614
615 uint32_t FontClient::GetNumberOfPointsPerOneUnitOfPointSize() const
616 {
617   if(mPlugin)
618   {
619     return mPlugin->GetNumberOfPointsPerOneUnitOfPointSize();
620   }
621   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
622   ;
623 }
624
625 FT_FaceRec_* FontClient::GetFreetypeFace(FontId fontId)
626 {
627   CreatePlugin();
628
629   return mPlugin->GetFreetypeFace(fontId);
630 }
631
632 FontDescription::Type FontClient::GetFontType(FontId fontId)
633 {
634   CreatePlugin();
635
636   return mPlugin->GetFontType(fontId);
637 }
638
639 bool FontClient::AddCustomFontDirectory(const FontPath& path)
640 {
641   CreatePlugin();
642
643   return mPlugin->AddCustomFontDirectory(path);
644 }
645
646 HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
647 {
648   CreatePlugin();
649
650   return mPlugin->GetHarfBuzzFont(fontId);
651 }
652
653 void FontClient::CreatePlugin()
654 {
655   std::scoped_lock lock(gMutex);
656   if(!mPlugin)
657   {
658     mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
659   }
660 }
661
662 } // namespace Internal
663
664 } // namespace TextAbstraction
665
666 } // namespace Dali