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