[dali_2.3.22] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-impl.cpp
1 /*
2  * Copyright (c) 2023 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::ClearCacheOnLocaleChanged()
339 {
340   if(mPlugin)
341   {
342     mPlugin->ClearCacheOnLocaleChanged();
343   }
344 }
345
346 void FontClient::SetDpi(unsigned int horizontalDpi, unsigned int verticalDpi)
347 {
348   mDpiHorizontal = horizontalDpi;
349   mDpiVertical   = verticalDpi;
350
351   // Allow DPI to be set without loading plugin
352   if(mPlugin)
353   {
354     mPlugin->SetDpi(horizontalDpi, verticalDpi);
355   }
356 }
357
358 void FontClient::GetDpi(unsigned int& horizontalDpi, unsigned int& verticalDpi)
359 {
360   horizontalDpi = mDpiHorizontal;
361   verticalDpi   = mDpiVertical;
362 }
363
364 int FontClient::GetDefaultFontSize()
365 {
366   int fontSize(-1);
367
368 #if defined(VCONF_ENABLED)
369   vconf_get_int(VCONFKEY_SETAPPL_ACCESSIBILITY_FONT_SIZE, &fontSize);
370 #endif
371
372   return fontSize;
373 }
374
375 void FontClient::ResetSystemDefaults()
376 {
377   CreatePlugin();
378
379   mPlugin->ResetSystemDefaults();
380 }
381
382 void FontClient::GetDefaultFonts(FontList& defaultFonts)
383 {
384   CreatePlugin();
385
386   mPlugin->GetDefaultFonts(defaultFonts);
387 }
388
389 void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
390 {
391   CreatePlugin();
392
393   mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
394 }
395
396 void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
397 {
398   CreatePlugin();
399
400   mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
401 }
402
403 void FontClient::InitDefaultFontDescription()
404 {
405   CreatePlugin();
406
407   mPlugin->InitDefaultFontDescription();
408 }
409
410 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
411 {
412   CreatePlugin();
413
414   mPlugin->GetDefaultPlatformFontDescription(fontDescription);
415 }
416
417 void FontClient::GetDescription(FontId fontId, FontDescription& fontDescription)
418 {
419   CreatePlugin();
420
421   mPlugin->GetDescription(fontId, fontDescription);
422 }
423
424 PointSize26Dot6 FontClient::GetPointSize(FontId fontId)
425 {
426   CreatePlugin();
427
428   return mPlugin->GetPointSize(fontId);
429 }
430
431 bool FontClient::IsCharacterSupportedByFont(FontId fontId, Character character)
432 {
433   CreatePlugin();
434
435   return mPlugin->IsCharacterSupportedByFont(fontId, character);
436 }
437
438 void FontClient::GetSystemFonts(FontList& systemFonts)
439 {
440   CreatePlugin();
441
442   mPlugin->GetSystemFonts(systemFonts);
443 }
444
445 FontId FontClient::FindDefaultFont(Character       charcode,
446                                    PointSize26Dot6 requestedPointSize,
447                                    bool            preferColor)
448 {
449   CreatePlugin();
450
451   return mPlugin->FindDefaultFont(charcode,
452                                   requestedPointSize,
453                                   preferColor);
454 }
455
456 FontId FontClient::FindFallbackFont(Character              charcode,
457                                     const FontDescription& preferredFontDescription,
458                                     PointSize26Dot6        requestedPointSize,
459                                     bool                   preferColor)
460 {
461   CreatePlugin();
462
463   return mPlugin->FindFallbackFont(charcode,
464                                    preferredFontDescription,
465                                    requestedPointSize,
466                                    preferColor);
467 }
468
469 bool FontClient::IsScalable(const FontPath& path)
470 {
471   CreatePlugin();
472
473   return mPlugin->IsScalable(path);
474 }
475
476 bool FontClient::IsScalable(const FontDescription& fontDescription)
477 {
478   CreatePlugin();
479
480   return mPlugin->IsScalable(fontDescription);
481 }
482
483 void FontClient::GetFixedSizes(const FontPath& path, Dali::Vector<PointSize26Dot6>& sizes)
484 {
485   CreatePlugin();
486
487   mPlugin->GetFixedSizes(path, sizes);
488 }
489
490 void FontClient::GetFixedSizes(const FontDescription&         fontDescription,
491                                Dali::Vector<PointSize26Dot6>& sizes)
492 {
493   CreatePlugin();
494
495   mPlugin->GetFixedSizes(fontDescription, sizes);
496 }
497
498 bool FontClient::HasItalicStyle(FontId fontId) const
499 {
500   if(!mPlugin)
501   {
502     return false;
503   }
504   return mPlugin->HasItalicStyle(fontId);
505 }
506
507 FontId FontClient::GetFontId(const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex)
508 {
509   CreatePlugin();
510
511   return mPlugin->GetFontIdByPath(path,
512                                   requestedPointSize,
513                                   faceIndex,
514                                   true);
515 }
516
517 FontId FontClient::GetFontId(const FontDescription& fontDescription,
518                              PointSize26Dot6        requestedPointSize,
519                              FaceIndex              faceIndex)
520 {
521   CreatePlugin();
522
523   return mPlugin->GetFontId(fontDescription,
524                             requestedPointSize,
525                             faceIndex);
526 }
527
528 FontId FontClient::GetFontId(const BitmapFont& bitmapFont)
529 {
530   CreatePlugin();
531
532   return mPlugin->GetFontId(bitmapFont);
533 }
534
535 void FontClient::GetFontMetrics(FontId fontId, FontMetrics& metrics)
536 {
537   CreatePlugin();
538
539   mPlugin->GetFontMetrics(fontId, metrics);
540 }
541
542 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode)
543 {
544   CreatePlugin();
545
546   return mPlugin->GetGlyphIndex(fontId, charcode);
547 }
548
549 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode, Character variantSelector)
550 {
551   CreatePlugin();
552
553   return mPlugin->GetGlyphIndex(fontId, charcode, variantSelector);
554 }
555
556 bool FontClient::GetGlyphMetrics(GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal)
557 {
558   CreatePlugin();
559
560   return mPlugin->GetGlyphMetrics(array, size, type, horizontal);
561 }
562
563 void FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, Dali::TextAbstraction::GlyphBufferData& data, int outlineWidth)
564 {
565   CreatePlugin();
566
567   mPlugin->CreateBitmap(fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth);
568 }
569
570 PixelData FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
571 {
572   CreatePlugin();
573
574   return mPlugin->CreateBitmap(fontId, glyphIndex, outlineWidth);
575 }
576
577 void FontClient::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
578 {
579   CreatePlugin();
580
581   mPlugin->CreateVectorBlob(fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
582 }
583
584 const GlyphInfo& FontClient::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
585 {
586   CreatePlugin();
587
588   return mPlugin->GetEllipsisGlyph(requestedPointSize);
589 }
590
591 bool FontClient::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
592 {
593   CreatePlugin();
594
595   return mPlugin->IsColorGlyph(fontId, glyphIndex);
596 }
597
598 GlyphIndex FontClient::CreateEmbeddedItem(const TextAbstraction::FontClient::EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
599 {
600   CreatePlugin();
601
602   return mPlugin->CreateEmbeddedItem(description, pixelFormat);
603 }
604
605 void FontClient::EnableAtlasLimitation(bool enabled)
606 {
607   CreatePlugin();
608   return mPlugin->EnableAtlasLimitation(enabled);
609 }
610
611 bool FontClient::IsAtlasLimitationEnabled() const
612 {
613   if(mPlugin)
614   {
615     return mPlugin->IsAtlasLimitationEnabled();
616   }
617   return TextAbstraction::FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED;
618 }
619
620 Size FontClient::GetMaximumTextAtlasSize() const
621 {
622   if(mPlugin)
623   {
624     return mPlugin->GetMaximumTextAtlasSize();
625   }
626   return TextAbstraction::FontClient::MAX_TEXT_ATLAS_SIZE;
627 }
628
629 Size FontClient::GetDefaultTextAtlasSize() const
630 {
631   if(mPlugin)
632   {
633     return mPlugin->GetDefaultTextAtlasSize();
634   }
635   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
636 }
637
638 Size FontClient::GetCurrentMaximumBlockSizeFitInAtlas() const
639 {
640   if(mPlugin)
641   {
642     return mPlugin->GetCurrentMaximumBlockSizeFitInAtlas();
643   }
644   return TextAbstraction::FontClient::DEFAULT_TEXT_ATLAS_SIZE;
645 }
646
647 bool FontClient::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
648 {
649   CreatePlugin();
650   return mPlugin->SetCurrentMaximumBlockSizeFitInAtlas(currentMaximumBlockSizeFitInAtlas);
651 }
652
653 uint32_t FontClient::GetNumberOfPointsPerOneUnitOfPointSize() const
654 {
655   if(mPlugin)
656   {
657     return mPlugin->GetNumberOfPointsPerOneUnitOfPointSize();
658   }
659   return TextAbstraction::FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE;
660   ;
661 }
662
663 FT_FaceRec_* FontClient::GetFreetypeFace(FontId fontId)
664 {
665   CreatePlugin();
666
667   return mPlugin->GetFreetypeFace(fontId);
668 }
669
670 FontDescription::Type FontClient::GetFontType(FontId fontId)
671 {
672   CreatePlugin();
673
674   return mPlugin->GetFontType(fontId);
675 }
676
677 bool FontClient::AddCustomFontDirectory(const FontPath& path)
678 {
679   CreatePlugin();
680
681   return mPlugin->AddCustomFontDirectory(path);
682 }
683
684 HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
685 {
686   CreatePlugin();
687
688   return mPlugin->GetHarfBuzzFont(fontId);
689 }
690
691 void FontClient::CreatePlugin()
692 {
693   std::scoped_lock lock(gMutex);
694   if(!mPlugin)
695   {
696     mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);
697   }
698 }
699
700 } // namespace Internal
701
702 } // namespace TextAbstraction
703
704 } // namespace Dali