8047822e4872191741557710c49346e2480377c2
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2018 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-plugin-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-list.h>
23 #include <dali/public-api/common/dali-vector.h>
24 #include <dali/public-api/common/vector-wrapper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/platform-abstraction.h>
27 #include <dali/internal/text/text-abstraction/font-client-helper.h>
28 #include <dali/internal/imaging/common/image-operations.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30
31 // EXTERNAL INCLUDES
32 #include <fontconfig/fontconfig.h>
33
34 namespace
35 {
36
37 #if defined(DEBUG_ENABLED)
38 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
39 #endif
40
41 /**
42  * Conversion from Fractional26.6 to float
43  */
44 const float FROM_266 = 1.0f / 64.0f;
45 const float POINTS_PER_INCH = 72.f;
46 const FT_Fixed FONT_SLANT_TANGENT = 0.221694663 * 0x10000; // For support software italic
47
48 const std::string FONT_FORMAT( "TrueType" );
49 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
50 const int DEFAULT_FONT_WIDTH  = 100; // normal
51 const int DEFAULT_FONT_WEIGHT =  80; // normal
52 const int DEFAULT_FONT_SLANT  =   0; // normal
53
54 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
55
56 // http://www.freedesktop.org/software/fontconfig/fontconfig-user.html
57
58 // NONE            -1  --> DEFAULT_FONT_WIDTH (NORMAL) will be used.
59 // ULTRA_CONDENSED 50
60 // EXTRA_CONDENSED 63
61 // CONDENSED       75
62 // SEMI_CONDENSED  87
63 // NORMAL         100
64 // SEMI_EXPANDED  113
65 // EXPANDED       125
66 // EXTRA_EXPANDED 150
67 // ULTRA_EXPANDED 200
68 const int FONT_WIDTH_TYPE_TO_INT[] = { -1, 50, 63, 75, 87, 100, 113, 125, 150, 200 };
69 const unsigned int NUM_FONT_WIDTH_TYPE = sizeof( FONT_WIDTH_TYPE_TO_INT ) / sizeof( int );
70
71 // NONE                       -1  --> DEFAULT_FONT_WEIGHT (NORMAL) will be used.
72 // THIN                        0
73 // ULTRA_LIGHT, EXTRA_LIGHT   40
74 // LIGHT                      50
75 // DEMI_LIGHT, SEMI_LIGHT     55
76 // BOOK                       75
77 // NORMAL, REGULAR            80
78 // MEDIUM                    100
79 // DEMI_BOLD, SEMI_BOLD      180
80 // BOLD                      200
81 // ULTRA_BOLD, EXTRA_BOLD    205
82 // BLACK, HEAVY, EXTRA_BLACK 210
83 const int FONT_WEIGHT_TYPE_TO_INT[] = { -1, 0, 40, 50, 55, 75, 80, 100, 180, 200, 205, 210 };
84 const unsigned int NUM_FONT_WEIGHT_TYPE = sizeof( FONT_WEIGHT_TYPE_TO_INT ) / sizeof( int );
85
86 // NONE             -1 --> DEFAULT_FONT_SLANT (NORMAL) will be used.
87 // NORMAL, ROMAN     0
88 // ITALIC          100
89 // OBLIQUE         110
90 const int FONT_SLANT_TYPE_TO_INT[] = { -1, 0, 100, 110 };
91 const unsigned int NUM_FONT_SLANT_TYPE = sizeof( FONT_SLANT_TYPE_TO_INT ) / sizeof( int );
92
93 } // namespace
94
95 using Dali::Vector;
96
97 namespace Dali
98 {
99
100 namespace TextAbstraction
101 {
102
103 namespace Internal
104 {
105
106 /**
107  * @brief Returns the FontWidth's enum index for the given width value.
108  *
109  * @param[in] width The width value.
110  *
111  * @return The FontWidth's enum index.
112  */
113 FontWidth::Type IntToWidthType( int width )
114 {
115   return static_cast<FontWidth::Type>( ValueToIndex( width, FONT_WIDTH_TYPE_TO_INT, NUM_FONT_WIDTH_TYPE - 1u ) );
116 }
117
118 /**
119  * @brief Returns the FontWeight's enum index for the given weight value.
120  *
121  * @param[in] weight The weight value.
122  *
123  * @return The FontWeight's enum index.
124  */
125 FontWeight::Type IntToWeightType( int weight )
126 {
127   return static_cast<FontWeight::Type>( ValueToIndex( weight, FONT_WEIGHT_TYPE_TO_INT, NUM_FONT_WEIGHT_TYPE - 1u ) );
128 }
129
130 /**
131  * @brief Returns the FontSlant's enum index for the given slant value.
132  *
133  * @param[in] slant The slant value.
134  *
135  * @return The FontSlant's enum index.
136  */
137 FontSlant::Type IntToSlantType( int slant )
138 {
139   return static_cast<FontSlant::Type>( ValueToIndex( slant, FONT_SLANT_TYPE_TO_INT, NUM_FONT_SLANT_TYPE - 1u ) );
140 }
141
142 FontClient::Plugin::FallbackCacheItem::FallbackCacheItem( FontDescription&& font, FontList* fallbackFonts, CharacterSetList* characterSets )
143 : fontDescription{ std::move( font ) },
144   fallbackFonts{ fallbackFonts },
145   characterSets{ characterSets }
146 {
147 }
148
149 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontDescription& fontDescription,
150                                                                         FontDescriptionId index )
151 : fontDescription{ fontDescription },
152   index{ index }
153 {
154 }
155
156 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( FontDescription&& fontDescription,
157                                                                         FontDescriptionId index )
158 : fontDescription{ std::move( fontDescription ) },
159   index{ index }
160 {
161 }
162
163 FontClient::Plugin::FontDescriptionSizeCacheItem::FontDescriptionSizeCacheItem( FontDescriptionId validatedFontId,
164                                                                                 PointSize26Dot6 requestedPointSize,
165                                                                                 FontId fontId )
166 : validatedFontId( validatedFontId ),
167   requestedPointSize( requestedPointSize ),
168   fontId( fontId )
169 {
170 }
171
172 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
173                                                           const FontPath& path,
174                                                           PointSize26Dot6 requestedPointSize,
175                                                           FaceIndex face,
176                                                           const FontMetrics& metrics )
177 : mFreeTypeFace( ftFace ),
178   mPath( path ),
179   mRequestedPointSize( requestedPointSize ),
180   mFaceIndex( face ),
181   mMetrics( metrics ),
182   mCharacterSet( nullptr ),
183   mFixedWidthPixels( 0.0f ),
184   mFixedHeightPixels( 0.0f ),
185   mVectorFontId( 0 ),
186   mIsFixedSizeBitmap( false ),
187   mHasColorTables( false )
188 {
189 }
190
191 FontClient::Plugin::FontFaceCacheItem::FontFaceCacheItem( FT_Face ftFace,
192                                                           const FontPath& path,
193                                                           PointSize26Dot6 requestedPointSize,
194                                                           FaceIndex face,
195                                                           const FontMetrics& metrics,
196                                                           float fixedWidth,
197                                                           float fixedHeight,
198                                                           bool hasColorTables )
199 : mFreeTypeFace( ftFace ),
200   mPath( path ),
201   mRequestedPointSize( requestedPointSize ),
202   mFaceIndex( face ),
203   mMetrics( metrics ),
204   mCharacterSet( nullptr ),
205   mFixedWidthPixels( fixedWidth ),
206   mFixedHeightPixels( fixedHeight ),
207   mVectorFontId( 0 ),
208   mIsFixedSizeBitmap( true ),
209   mHasColorTables( hasColorTables )
210 {
211 }
212
213 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
214                             unsigned int verticalDpi )
215 : mFreeTypeLibrary( nullptr ),
216   mDpiHorizontal( horizontalDpi ),
217   mDpiVertical( verticalDpi ),
218   mDefaultFontDescription(),
219   mSystemFonts(),
220   mDefaultFonts(),
221   mFontFaceCache(),
222   mValidatedFontCache(),
223   mFontDescriptionCache( 1u ),
224   mCharacterSetCache(),
225   mFontDescriptionSizeCache(),
226   mVectorFontCache( nullptr ),
227   mEllipsisCache(),
228   mDefaultFontDescriptionCached( false )
229 {
230   mCharacterSetCache.Resize( 1u );
231
232   int error = FT_Init_FreeType( &mFreeTypeLibrary );
233   if( FT_Err_Ok != error )
234   {
235     DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Init error: %d\n", error );
236   }
237
238 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
239   mVectorFontCache = new VectorFontCache( mFreeTypeLibrary );
240 #endif
241 }
242
243 FontClient::Plugin::~Plugin()
244 {
245   for( auto& item : mFallbackCache )
246   {
247     if( item.fallbackFonts )
248     {
249       delete item.fallbackFonts;
250       delete item.characterSets;
251       item.fallbackFonts = nullptr;
252       item.characterSets = nullptr;
253     }
254   }
255
256 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
257   delete mVectorFontCache;
258 #endif
259   DestroyMatchedPatterns();
260   FT_Done_FreeType( mFreeTypeLibrary );
261 }
262
263 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
264                                  unsigned int verticalDpi )
265 {
266   mDpiHorizontal = horizontalDpi;
267   mDpiVertical = verticalDpi;
268 }
269
270 void FontClient::Plugin::ResetSystemDefaults()
271 {
272   mDefaultFontDescriptionCached = false;
273 }
274
275 void FontClient::Plugin::SetFontList( const FontDescription& fontDescription, FontList& fontList, CharacterSetList& characterSetList )
276 {
277   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::SetFontList\n" );
278   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
279   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
280   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
281   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
282
283   fontList.clear();
284
285   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
286
287   FcResult result = FcResultMatch;
288
289   // Match the pattern.
290   FcFontSet* fontSet = FcFontSort( nullptr /* use default configure */,
291                                    fontFamilyPattern,
292                                    false /* don't trim */,
293                                    nullptr,
294                                    &result );
295
296   if( nullptr != fontSet )
297   {
298     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts found : [%d]\n", fontSet->nfont );
299     // Reserve some space to avoid reallocations.
300     fontList.reserve( fontSet->nfont );
301
302     for( int i = 0u; i < fontSet->nfont; ++i )
303     {
304       FcPattern* fontPattern = fontSet->fonts[i];
305
306       FontPath path;
307
308       // Skip fonts with no path
309       if( GetFcString( fontPattern, FC_FILE, path ) )
310       {
311         FcCharSet* characterSet = nullptr;
312         FcPatternGetCharSet( fontPattern, FC_CHARSET, 0u, &characterSet );
313
314         characterSetList.PushBack( characterSet );
315         fontList.push_back( FontDescription() );
316         FontDescription& newFontDescription = fontList.back();
317
318         newFontDescription.path = std::move( path );
319
320         int width = 0;
321         int weight = 0;
322         int slant = 0;
323         GetFcString( fontPattern, FC_FAMILY, newFontDescription.family );
324         GetFcInt( fontPattern, FC_WIDTH, width );
325         GetFcInt( fontPattern, FC_WEIGHT, weight );
326         GetFcInt( fontPattern, FC_SLANT, slant );
327         newFontDescription.width = IntToWidthType( width );
328         newFontDescription.weight = IntToWeightType( weight );
329         newFontDescription.slant = IntToSlantType( slant );
330
331         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", newFontDescription.family.c_str() );
332         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", newFontDescription.path.c_str() );
333         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[newFontDescription.width] );
334         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[newFontDescription.weight] );
335         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[newFontDescription.slant] );
336       }
337     }
338
339     FcFontSetDestroy( fontSet );
340   }
341   else
342   {
343     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  No fonts found.\n" );
344   }
345
346   FcPatternDestroy( fontFamilyPattern );
347   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::SetFontList\n" );
348 }
349
350 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
351 {
352   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultFonts\n" );
353
354   if( mDefaultFonts.empty() )
355   {
356     FontDescription fontDescription;
357     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;  // todo This could be set to the Platform font
358     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
359     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
360     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
361     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
362   }
363
364   defaultFonts = mDefaultFonts;
365
366   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of default fonts : [%d]\n", mDefaultFonts.size() );
367   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultFonts\n" );
368 }
369
370 void FontClient::Plugin::GetDefaultPlatformFontDescription( FontDescription& fontDescription )
371 {
372   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDefaultPlatformFontDescription\n");
373
374   if( !mDefaultFontDescriptionCached )
375   {
376     // Clear any font config stored info in the caches.
377     mDefaultFontCharacterSets.Clear();
378     mCharacterSetCache.Clear();
379
380     for( auto& item : mFallbackCache )
381     {
382       item.characterSets->Clear();
383     }
384
385     for( auto& item : mFontFaceCache )
386     {
387       // Set the character set pointer as null. Will be created again the next time IsCharacterSupportedByFont()
388       item.mCharacterSet = nullptr;
389     }
390
391     // FcInitBringUptoDate did not seem to reload config file as was still getting old default font.
392     FcInitReinitialize();
393
394     FcPattern* matchPattern = FcPatternCreate();
395
396     if( matchPattern )
397     {
398       FcConfigSubstitute( nullptr, matchPattern, FcMatchPattern );
399       FcDefaultSubstitute( matchPattern );
400
401       FcCharSet* characterSet = nullptr;
402       MatchFontDescriptionToPattern( matchPattern, mDefaultFontDescription, &characterSet );
403       FcPatternDestroy( matchPattern );
404     }
405
406     // Create again the character sets as they are not valid after FcInitReinitialize()
407
408     for( const auto& description : mDefaultFonts )
409     {
410       mDefaultFontCharacterSets.PushBack( CreateCharacterSetFromDescription( description ) );
411     }
412
413     for( const auto& description : mFontDescriptionCache )
414     {
415       mCharacterSetCache.PushBack( CreateCharacterSetFromDescription( description ) );
416     }
417
418     for( auto& item : mFallbackCache )
419     {
420       if( nullptr != item.fallbackFonts )
421       {
422         if( nullptr == item.characterSets )
423         {
424           item.characterSets = new CharacterSetList;
425         }
426
427         for( const auto& description : *( item.fallbackFonts ) )
428         {
429           item.characterSets->PushBack( CreateCharacterSetFromDescription( description ) );
430         }
431       }
432     }
433
434     mDefaultFontDescriptionCached = true;
435   }
436
437   fontDescription.path   = mDefaultFontDescription.path;
438   fontDescription.family = mDefaultFontDescription.family;
439   fontDescription.width  = mDefaultFontDescription.width;
440   fontDescription.weight = mDefaultFontDescription.weight;
441   fontDescription.slant  = mDefaultFontDescription.slant;
442
443   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
444   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
445   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
446   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
447   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
448   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDefaultPlatformFontDescription\n");
449 }
450
451 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
452 {
453   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetSystemFonts\n");
454
455   if( mSystemFonts.empty() )
456   {
457     InitSystemFonts();
458   }
459
460   systemFonts = mSystemFonts;
461   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : [%d]\n", mSystemFonts.size() );
462   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetSystemFonts\n");
463 }
464
465 void FontClient::Plugin::GetDescription( FontId id,
466                                          FontDescription& fontDescription ) const
467 {
468   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetDescription\n");
469   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
470
471   for( const auto& item : mFontDescriptionSizeCache )
472   {
473     if( item.fontId == id )
474     {
475       fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
476
477       DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
478       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
479       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
480       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
481       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
482       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
483       return;
484     }
485   }
486
487   DALI_LOG_INFO( gLogFilter, Debug::General, "  No description found for the font ID %d\n", id );
488   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetDescription\n");
489 }
490
491 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
492 {
493   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetPointSize\n");
494   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
495   const FontId index = id - 1u;
496
497   if( ( id > 0u ) &&
498       ( index < mFontFaceCache.size() ) )
499   {
500     DALI_LOG_INFO( gLogFilter, Debug::General, "  point size : %d\n", ( *( mFontFaceCache.begin() + index ) ).mRequestedPointSize );
501     DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
502     return ( *( mFontFaceCache.begin() + index ) ).mRequestedPointSize;
503   }
504   else
505   {
506     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font ID %d\n", id );
507   }
508
509   DALI_LOG_INFO( gLogFilter, Debug::General, "  default point size : %d\n", TextAbstraction::FontClient::DEFAULT_POINT_SIZE );
510   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetPointSize\n");
511   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
512 }
513
514 bool FontClient::Plugin::IsCharacterSupportedByFont( FontId fontId, Character character )
515 {
516   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::IsCharacterSupportedByFont\n");
517   DALI_LOG_INFO( gLogFilter, Debug::General, "    font id : %d\n", fontId );
518   DALI_LOG_INFO( gLogFilter, Debug::General, "  character : %p\n", character );
519
520   if( ( fontId < 1u ) || ( fontId > mFontFaceCache.size() ) )
521   {
522     DALI_LOG_INFO( gLogFilter, Debug::General, "  Invalid font id. Number of items in the cache: %d\n",mFontFaceCache.size());
523     DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
524     return false;
525   }
526
527   --fontId;
528
529   bool isSupported = false;
530
531   FontFaceCacheItem& cacheItem = mFontFaceCache[fontId];
532
533   if( nullptr == cacheItem.mCharacterSet )
534   {
535     // Create again the character set.
536     // It can be null if the ResetSystemDefaults() method has been called.
537
538     FontDescription description;
539     description.path = cacheItem.mPath;
540     description.family = std::move( FontFamily( cacheItem.mFreeTypeFace->family_name ) );
541     description.weight = FontWeight::NONE;
542     description.width = FontWidth::NONE;
543     description.slant = FontSlant::NONE;
544
545     // Note FreeType doesn't give too much info to build a proper font style.
546     if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_ITALIC )
547     {
548       description.slant = FontSlant::ITALIC;
549     }
550     if( cacheItem.mFreeTypeFace->style_flags & FT_STYLE_FLAG_BOLD )
551     {
552       description.weight = FontWeight::BOLD;
553     }
554
555     cacheItem.mCharacterSet = CreateCharacterSetFromDescription( description );
556   }
557
558   isSupported = FcCharSetHasChar( cacheItem.mCharacterSet, character );
559
560   DALI_LOG_INFO( gLogFilter, Debug::General, "  is supported : %s\n", (isSupported ? "true" : "false") );
561   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::IsCharacterSupportedByFont\n");
562   return isSupported;
563 }
564
565 FontId FontClient::Plugin::FindFontForCharacter( const FontList& fontList,
566                                                  const CharacterSetList& characterSetList,
567                                                  Character character,
568                                                  PointSize26Dot6 requestedPointSize,
569                                                  bool preferColor )
570 {
571   DALI_ASSERT_DEBUG( ( fontList.size() == characterSetList.Count() ) && "FontClient::Plugin::FindFontForCharacter. Different number of fonts and character sets." );
572
573   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFontForCharacter\n" );
574   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", character );
575   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
576   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
577
578   FontId fontId = 0u;
579   bool foundColor = false;
580
581   DALI_LOG_INFO( gLogFilter, Debug::General, "  number of fonts : %d\n", fontList.size() );
582
583   // Traverse the list of fonts.
584   // Check for each font if supports the character.
585   for( unsigned int index = 0u, numberOfFonts = fontList.size(); index < numberOfFonts; ++index )
586   {
587     const FontDescription& description = fontList[index];
588     const FcCharSet* const characterSet = characterSetList[index];
589
590     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", description.family.c_str() );
591     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", description.path.c_str() );
592     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[description.width] );
593     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[description.weight] );
594     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[description.slant] );
595
596     bool foundInRanges = false;
597     if( nullptr != characterSet )
598     {
599       foundInRanges = FcCharSetHasChar( characterSet, character );
600     }
601
602     if( foundInRanges )
603     {
604       fontId = GetFontId( description,
605                           requestedPointSize,
606                           0u );
607
608       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "     font id : %d\n", fontId );
609
610       if( preferColor )
611       {
612         if( ( fontId > 0 ) &&
613             ( fontId - 1u < mFontFaceCache.size() ) )
614         {
615           const FontFaceCacheItem& item = mFontFaceCache[fontId - 1u];
616
617           foundColor = item.mHasColorTables;
618         }
619
620         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  foundColor : %s\n", ( foundColor ? "true" : "false" ) );
621       }
622
623       // Keep going unless we prefer a different (color) font.
624       if( !preferColor || foundColor )
625       {
626         break;
627       }
628     }
629   }
630
631   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
632   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFontForCharacter\n" );
633   return fontId;
634 }
635
636 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
637                                             PointSize26Dot6 requestedPointSize,
638                                             bool preferColor )
639 {
640   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindDefaultFont\n" );
641   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
642   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
643   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
644
645   FontId fontId(0);
646
647   // Create the list of default fonts if it has not been created.
648   if( mDefaultFonts.empty() )
649   {
650     FontDescription fontDescription;
651     fontDescription.family = DEFAULT_FONT_FAMILY_NAME;
652     fontDescription.width = IntToWidthType( DEFAULT_FONT_WIDTH );
653     fontDescription.weight = IntToWeightType( DEFAULT_FONT_WEIGHT );
654     fontDescription.slant = IntToSlantType( DEFAULT_FONT_SLANT );
655
656     SetFontList( fontDescription, mDefaultFonts, mDefaultFontCharacterSets );
657   }
658   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of default fonts : %d\n", mDefaultFonts.size() );
659
660
661   // Traverse the list of default fonts.
662   // Check for each default font if supports the character.
663   fontId = FindFontForCharacter( mDefaultFonts, mDefaultFontCharacterSets, charcode, requestedPointSize, preferColor );
664
665   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
666   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindDefaultFont\n" );
667
668   return fontId;
669 }
670
671 FontId FontClient::Plugin::FindFallbackFont( Character charcode,
672                                              const FontDescription& preferredFontDescription,
673                                              PointSize26Dot6 requestedPointSize,
674                                              bool preferColor )
675 {
676   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFont\n" );
677   DALI_LOG_INFO( gLogFilter, Debug::General, "           character : %p\n", charcode );
678   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
679   DALI_LOG_INFO( gLogFilter, Debug::General, "         preferColor : %s\n", ( preferColor ? "true" : "false" ) );
680
681   // The font id to be returned.
682   FontId fontId = 0u;
683
684   FontDescription fontDescription;
685
686   // Fill the font description with the preferred font description and complete with the defaults.
687   fontDescription.family = preferredFontDescription.family.empty() ? DEFAULT_FONT_FAMILY_NAME : preferredFontDescription.family;
688   fontDescription.weight = ( ( FontWeight::NONE == preferredFontDescription.weight ) ? IntToWeightType( DEFAULT_FONT_WEIGHT ) : preferredFontDescription.weight );
689   fontDescription.width = ( ( FontWidth::NONE == preferredFontDescription.width ) ? IntToWidthType( DEFAULT_FONT_WIDTH ) : preferredFontDescription.width );
690   fontDescription.slant = ( ( FontSlant::NONE == preferredFontDescription.slant ) ? IntToSlantType( DEFAULT_FONT_SLANT ) : preferredFontDescription.slant );
691
692   DALI_LOG_INFO( gLogFilter, Debug::General, "  preferredFontDescription --> fontDescription\n" );
693   DALI_LOG_INFO( gLogFilter, Debug::General, "  [%s] --> [%s]\n", preferredFontDescription.family.c_str(), fontDescription.family.c_str() );
694   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWeight::Name[preferredFontDescription.weight], FontWeight::Name[fontDescription.weight] );
695   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontWidth::Name[preferredFontDescription.width], FontWidth::Name[fontDescription.width] );
696   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  [%s] --> [%s]\n", FontSlant::Name[preferredFontDescription.slant], FontSlant::Name[fontDescription.slant] );
697
698   // Check first if the font's description has been queried before.
699   FontList* fontList = nullptr;
700   CharacterSetList* characterSetList = nullptr;
701
702   if( !FindFallbackFontList( fontDescription, fontList, characterSetList ) )
703   {
704     fontList = new FontList;
705     characterSetList = new CharacterSetList;
706
707     SetFontList( fontDescription, *fontList, *characterSetList );
708
709     // Add the font-list to the cache.
710     mFallbackCache.push_back( std::move( FallbackCacheItem( std::move( fontDescription ), fontList, characterSetList ) ) );
711   }
712
713   if( fontList && characterSetList )
714   {
715     fontId = FindFontForCharacter( *fontList, *characterSetList, charcode, requestedPointSize, preferColor );
716   }
717
718   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
719   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFont\n");
720   return fontId;
721 }
722
723 FontId FontClient::Plugin::GetFontId( const FontPath& path,
724                                       PointSize26Dot6 requestedPointSize,
725                                       FaceIndex faceIndex,
726                                       bool cacheDescription )
727 {
728   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
729   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
730   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
731
732   FontId id( 0 );
733
734   if( nullptr != mFreeTypeLibrary )
735   {
736     FontId foundId(0);
737     if( FindFont( path, requestedPointSize, faceIndex, foundId ) )
738     {
739       id = foundId;
740     }
741     else
742     {
743       id = CreateFont( path, requestedPointSize, faceIndex, cacheDescription );
744     }
745   }
746
747   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
748   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
749
750   return id;
751 }
752
753 FontId FontClient::Plugin::GetFontId( const FontDescription& fontDescription,
754                                       PointSize26Dot6 requestedPointSize,
755                                       FaceIndex faceIndex )
756 {
757   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetFontId\n" );
758   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
759   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
760   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
761   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
762   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
763   DALI_LOG_INFO( gLogFilter, Debug::General, "   requestedPointSize : %d\n", requestedPointSize );
764
765   // This method uses three vectors which caches:
766   // * Pairs of non validated font descriptions and an index to a vector with paths to font file names.
767   // * The path to font file names.
768   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
769
770   // 1) Checks in the cache if the font's description has been validated before.
771   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
772   //    retrieves using font config a path to a font file name which matches with the
773   //    font's description. The path is stored in the cache.
774   //
775   // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
776   //    font file names' exists. If exists, it gets the font id. If it doesn't it calls
777   //    the GetFontId() method with the path to the font file name and the point size to
778   //    get the font id.
779
780   // The font id to be returned.
781   FontId fontId = 0u;
782
783   // Check first if the font's description have been validated before.
784   FontDescriptionId validatedFontId = 0u;
785
786   if( !FindValidatedFont( fontDescription,
787                           validatedFontId ) )
788   {
789     // Use font config to validate the font's description.
790     ValidateFont( fontDescription,
791                   validatedFontId );
792   }
793
794   // Check if exists a pair 'validatedFontId, requestedPointSize' in the cache.
795   if( !FindFont( validatedFontId, requestedPointSize, fontId ) )
796   {
797     // Retrieve the font file name path.
798     const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
799
800     // Retrieve the font id. Do not cache the description as it has been already cached.
801     fontId = GetFontId( description.path,
802                         requestedPointSize,
803                         faceIndex,
804                         false );
805
806     mFontFaceCache[fontId-1u].mCharacterSet = mCharacterSetCache[validatedFontId];
807
808     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
809     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
810                                                                        requestedPointSize,
811                                                                        fontId ) );
812   }
813
814   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", fontId );
815   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetFontId\n" );
816
817   return fontId;
818 }
819
820 void FontClient::Plugin::ValidateFont( const FontDescription& fontDescription,
821                                        FontDescriptionId& validatedFontId )
822 {
823   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::ValidateFont\n" );
824   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
825   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
826   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
827   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
828   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
829
830   // Create a font pattern.
831   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
832
833   FontDescription description;
834
835   FcCharSet* characterSet = nullptr;
836   bool matched = MatchFontDescriptionToPattern( fontFamilyPattern, description, &characterSet );
837   FcPatternDestroy( fontFamilyPattern );
838
839   if( matched && ( nullptr != characterSet ) )
840   {
841     // Set the index to the vector of paths to font file names.
842     validatedFontId = mFontDescriptionCache.size();
843
844     DALI_LOG_INFO( gLogFilter, Debug::General, "  matched description; family : [%s]\n", description.family.c_str() );
845     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                         path : [%s]\n", description.path.c_str() );
846     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        width : [%s]\n", FontWidth::Name[description.width] );
847     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                       weight : [%s]\n", FontWeight::Name[description.weight] );
848     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                        slant : [%s]\n\n", FontSlant::Name[description.slant] );
849     DALI_LOG_INFO( gLogFilter, Debug::General, "  validatedFontId : %d\n", validatedFontId );
850
851     // Add the path to the cache.
852     mFontDescriptionCache.push_back( description );
853     mCharacterSetCache.PushBack( characterSet );
854
855     // Cache the index and the matched font's description.
856     FontDescriptionCacheItem item( description,
857                                    validatedFontId );
858
859     mValidatedFontCache.push_back( std::move( item ) );
860
861     if( ( fontDescription.family != description.family ) ||
862         ( fontDescription.width != description.width )   ||
863         ( fontDescription.weight != description.weight ) ||
864         ( fontDescription.slant != description.slant ) )
865     {
866       // Cache the given font's description if it's different than the matched.
867       FontDescriptionCacheItem item( fontDescription,
868                                      validatedFontId );
869
870       mValidatedFontCache.push_back( std::move( item ) );
871     }
872   }
873   else
874   {
875     DALI_LOG_INFO( gLogFilter, Debug::General, "  font validation failed for font [%s]\n", fontDescription.family.c_str() );
876   }
877
878   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::ValidateFont\n" );
879 }
880
881 void FontClient::Plugin::GetFontMetrics( FontId fontId,
882                                          FontMetrics& metrics )
883 {
884   if( ( fontId > 0 ) &&
885       ( fontId - 1u < mFontFaceCache.size() ) )
886   {
887     const FontFaceCacheItem& font = mFontFaceCache[fontId-1];
888
889     metrics = font.mMetrics;
890
891     // Adjust the metrics if the fixed-size font should be down-scaled
892     if( font.mIsFixedSizeBitmap )
893     {
894       const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
895
896       if( desiredFixedSize > 0.f )
897       {
898         const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
899
900         metrics.ascender = floorf( metrics.ascender * scaleFactor );
901         metrics.descender = floorf( metrics.descender * scaleFactor );
902         metrics.height = floorf( metrics.height * scaleFactor );
903         metrics.underlinePosition = floorf( metrics.underlinePosition * scaleFactor );
904         metrics.underlineThickness = floorf( metrics.underlineThickness * scaleFactor );
905       }
906     }
907   }
908   else
909   {
910     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFontMetrics. Invalid font id : %d\n", fontId );
911   }
912 }
913
914 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
915                                               Character charcode )
916 {
917   GlyphIndex index = 0u;
918
919   if( ( fontId > 0u ) &&
920       ( fontId - 1u < mFontFaceCache.size() ) )
921   {
922     FT_Face ftFace = mFontFaceCache[fontId-1u].mFreeTypeFace;
923
924     index = FT_Get_Char_Index( ftFace, charcode );
925   }
926
927   return index;
928 }
929
930 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
931                                           uint32_t size,
932                                           GlyphType type,
933                                           bool horizontal )
934 {
935   if( VECTOR_GLYPH == type )
936   {
937     return GetVectorMetrics( array, size, horizontal );
938   }
939
940   return GetBitmapMetrics( array, size, horizontal );
941 }
942
943 bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array,
944                                            uint32_t size,
945                                            bool horizontal )
946 {
947   bool success( true );
948
949   for( unsigned int i=0; i<size; ++i )
950   {
951     GlyphInfo& glyph = array[i];
952
953     FontId fontId = glyph.fontId;
954
955     if( fontId > 0 &&
956         fontId-1 < mFontFaceCache.size() )
957     {
958       const FontFaceCacheItem& font = mFontFaceCache[fontId-1];
959
960       FT_Face ftFace = font.mFreeTypeFace;
961
962 #ifdef FREETYPE_BITMAP_SUPPORT
963       // Check to see if we should be loading a Fixed Size bitmap?
964       if ( font.mIsFixedSizeBitmap )
965       {
966         int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_COLOR );
967         if ( FT_Err_Ok == error )
968         {
969           glyph.width = font.mFixedWidthPixels;
970           glyph.height = font.mFixedHeightPixels;
971           glyph.advance = font.mFixedWidthPixels;
972           glyph.xBearing = 0.0f;
973           glyph.yBearing = font.mFixedHeightPixels;
974
975           // Adjust the metrics if the fixed-size font should be down-scaled
976           const float desiredFixedSize =  static_cast<float>( font.mRequestedPointSize ) * FROM_266 / POINTS_PER_INCH * mDpiVertical;
977
978           if( desiredFixedSize > 0.f )
979           {
980             const float scaleFactor = desiredFixedSize / static_cast<float>( font.mFixedHeightPixels );
981
982             glyph.width = floorf( glyph.width * scaleFactor );
983             glyph.height = floorf( glyph.height * scaleFactor );
984             glyph.advance = floorf( glyph.advance * scaleFactor );
985             glyph.xBearing = floorf( glyph.xBearing * scaleFactor );
986             glyph.yBearing = floorf( glyph.yBearing * scaleFactor );
987
988             glyph.scaleFactor = scaleFactor;
989           }
990         }
991         else
992         {
993           DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetBitmapMetrics. FreeType Bitmap Load_Glyph error %d\n", error );
994           success = false;
995         }
996       }
997       else
998 #endif
999       {
1000         int error = FT_Load_Glyph( ftFace, glyph.index, FT_LOAD_NO_AUTOHINT );
1001
1002         if( FT_Err_Ok == error )
1003         {
1004           glyph.width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
1005           glyph.height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
1006           if( horizontal )
1007           {
1008             glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
1009             glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
1010           }
1011           else
1012           {
1013             glyph.xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
1014             glyph.yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
1015           }
1016         }
1017         else
1018         {
1019           success = false;
1020         }
1021       }
1022     }
1023     else
1024     {
1025       success = false;
1026     }
1027   }
1028
1029   return success;
1030 }
1031
1032 bool FontClient::Plugin::GetVectorMetrics( GlyphInfo* array,
1033                                            uint32_t size,
1034                                            bool horizontal )
1035 {
1036 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1037   bool success( true );
1038
1039   for( unsigned int i=0; i<size; ++i )
1040   {
1041     FontId fontId = array[i].fontId;
1042
1043     if( fontId > 0 &&
1044         fontId-1 < mFontFaceCache.size() )
1045     {
1046       FontFaceCacheItem& font = mFontFaceCache[fontId-1];
1047
1048       if( ! font.mVectorFontId )
1049       {
1050         font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1051       }
1052
1053       mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] );
1054
1055       // Vector metrics are in EMs, convert to pixels
1056       const float scale = ( static_cast<float>( font.mRequestedPointSize ) * FROM_266 ) * static_cast<float>( mDpiVertical ) / POINTS_PER_INCH;
1057       array[i].width    *= scale;
1058       array[i].height   *= scale;
1059       array[i].xBearing *= scale;
1060       array[i].yBearing *= scale;
1061       array[i].advance  *= scale;
1062     }
1063     else
1064     {
1065       success = false;
1066     }
1067   }
1068
1069   return success;
1070 #else
1071   return false;
1072 #endif
1073 }
1074
1075 void FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, bool softwareItalic, bool softwareBold, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth )
1076 {
1077   if( ( fontId > 0 ) &&
1078       ( fontId - 1u < mFontFaceCache.size() ) )
1079   {
1080     FT_Face ftFace = mFontFaceCache[fontId - 1u].mFreeTypeFace;
1081
1082     FT_Error error;
1083
1084 #ifdef FREETYPE_BITMAP_SUPPORT
1085     // Check to see if this is fixed size bitmap
1086     if ( mFontFaceCache[fontId - 1u].mIsFixedSizeBitmap )
1087     {
1088       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1089     }
1090     else
1091 #endif
1092     {
1093       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_NO_AUTOHINT );
1094     }
1095     if( FT_Err_Ok == error )
1096     {
1097       FT_Glyph glyph;
1098
1099       if( softwareBold )
1100       {
1101         FT_GlyphSlot_Embolden(ftFace->glyph);
1102       }
1103
1104       if( softwareItalic )
1105       {
1106         // FT Matrix uses 16.16 fixed-point format
1107         FT_Matrix transform = {0x10000, FONT_SLANT_TANGENT, 0x00000, 0x10000};
1108         FT_Outline_Transform(&ftFace->glyph->outline, &transform);
1109       }
1110
1111       error = FT_Get_Glyph( ftFace->glyph, &glyph );
1112
1113       // Convert to bitmap if necessary
1114       if ( FT_Err_Ok == error )
1115       {
1116         if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
1117         {
1118           // Check whether we should create a bitmap for the outline
1119           if( glyph->format == FT_GLYPH_FORMAT_OUTLINE && outlineWidth > 0 )
1120           {
1121             // Set up a stroker
1122             FT_Stroker stroker;
1123             error = FT_Stroker_New(mFreeTypeLibrary, &stroker );
1124
1125             if ( FT_Err_Ok == error )
1126             {
1127               FT_Stroker_Set( stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
1128               error = FT_Glyph_StrokeBorder( &glyph, stroker, 0, 1 );
1129
1130               if ( FT_Err_Ok == error )
1131               {
1132                 FT_Stroker_Done( stroker );
1133               }
1134               else
1135               {
1136                 DALI_LOG_ERROR( "FT_Glyph_StrokeBorder Failed with error: %d\n", error );
1137               }
1138             }
1139             else
1140             {
1141               DALI_LOG_ERROR( "FT_Stroker_New Failed with error: %d\n", error );
1142             }
1143           }
1144
1145           error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
1146           if ( FT_Err_Ok == error )
1147           {
1148             FT_BitmapGlyph bitmapGlyph = reinterpret_cast< FT_BitmapGlyph >( glyph );
1149             ConvertBitmap( data, bitmapGlyph->bitmap );
1150           }
1151           else
1152           {
1153             DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error );
1154           }
1155         }
1156         else
1157         {
1158           ConvertBitmap( data, ftFace->glyph->bitmap );
1159         }
1160
1161         // Created FT_Glyph object must be released with FT_Done_Glyph
1162         FT_Done_Glyph( glyph );
1163       }
1164     }
1165     else
1166     {
1167       DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Load_Glyph Failed with error: %d\n", error );
1168     }
1169   }
1170 }
1171
1172 PixelData FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex, int outlineWidth )
1173 {
1174   TextAbstraction::FontClient::GlyphBufferData data;
1175
1176   CreateBitmap( fontId, glyphIndex, false, false, data, outlineWidth );
1177
1178   return PixelData::New( data.buffer,
1179                          data.width * data.height * Pixel::GetBytesPerPixel( data.format ),
1180                          data.width,
1181                          data.height,
1182                          data.format,
1183                          PixelData::DELETE_ARRAY );
1184 }
1185
1186 void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight )
1187 {
1188   blob = nullptr;
1189   blobLength = 0;
1190
1191 #ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING
1192   if( fontId > 0 &&
1193       fontId-1 < mFontFaceCache.size() )
1194   {
1195     FontFaceCacheItem& font = mFontFaceCache[fontId-1];
1196
1197     if( ! font.mVectorFontId )
1198     {
1199       font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath );
1200     }
1201
1202     mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight );
1203   }
1204 #endif
1205 }
1206
1207 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 requestedPointSize )
1208 {
1209   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::GetEllipsisGlyph\n" );
1210   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize %d.\n", requestedPointSize );
1211
1212   // First look into the cache if there is an ellipsis glyph for the requested point size.
1213   for( const auto& item : mEllipsisCache )
1214   {
1215     if( fabsf( item.requestedPointSize - requestedPointSize ) < Math::MACHINE_EPSILON_1000 )
1216     {
1217       // Use the glyph in the cache.
1218
1219       DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1220       DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1221       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1222
1223       return item.glyph;
1224     }
1225   }
1226
1227   // No glyph has been found. Create one.
1228   mEllipsisCache.PushBack( EllipsisItem() );
1229   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
1230
1231   item.requestedPointSize = requestedPointSize;
1232
1233   // Find a font for the ellipsis glyph.
1234   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
1235                                        requestedPointSize,
1236                                        false );
1237
1238   // Set the character index to access the glyph inside the font.
1239   item.glyph.index = FT_Get_Char_Index( mFontFaceCache[item.glyph.fontId-1].mFreeTypeFace,
1240                                         ELLIPSIS_CHARACTER );
1241
1242   GetBitmapMetrics( &item.glyph, 1u, true );
1243
1244   DALI_LOG_INFO( gLogFilter, Debug::General, "  glyph id %d found in the cache.\n", item.glyph.index );
1245   DALI_LOG_INFO( gLogFilter, Debug::General, "      font %d.\n", item.glyph.fontId );
1246   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::GetEllipsisGlyph\n" );
1247
1248   return item.glyph;
1249 }
1250
1251 bool FontClient::Plugin::IsColorGlyph( FontId fontId, GlyphIndex glyphIndex )
1252 {
1253   FT_Error error = -1;
1254
1255 #ifdef FREETYPE_BITMAP_SUPPORT
1256   if( ( fontId > 0 ) &&
1257       ( fontId - 1u < mFontFaceCache.size() ) )
1258   {
1259     const FontFaceCacheItem& item = mFontFaceCache[fontId - 1u];
1260     FT_Face ftFace = item.mFreeTypeFace;
1261
1262     // Check to see if this is fixed size bitmap
1263     if( item.mHasColorTables )
1264     {
1265       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
1266     }
1267   }
1268 #endif
1269
1270   return FT_Err_Ok == error;
1271 }
1272
1273 FT_FaceRec_* FontClient::Plugin::GetFreetypeFace( FontId fontId )
1274 {
1275   FT_Face fontFace = nullptr;
1276
1277   if( ( fontId > 0u ) &&
1278       ( fontId - 1u < mFontFaceCache.size() ) )
1279   {
1280     fontFace = mFontFaceCache[fontId - 1u].mFreeTypeFace;
1281   }
1282
1283   return fontFace;
1284 }
1285
1286 void FontClient::Plugin::InitSystemFonts()
1287 {
1288   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::InitSystemFonts\n" );
1289
1290   FcFontSet* fontSet = GetFcFontSet();
1291
1292   if( fontSet )
1293   {
1294     DALI_LOG_INFO( gLogFilter, Debug::General, "  number of system fonts : %d\n", fontSet->nfont );
1295
1296     // Reserve some space to avoid reallocations.
1297     mSystemFonts.reserve( fontSet->nfont );
1298
1299     for( int i = 0u; i < fontSet->nfont; ++i )
1300     {
1301       FcPattern* fontPattern = fontSet->fonts[i];
1302
1303       FontPath path;
1304
1305       // Skip fonts with no path
1306       if( GetFcString( fontPattern, FC_FILE, path ) )
1307       {
1308         mSystemFonts.push_back( FontDescription() );
1309         FontDescription& fontDescription = mSystemFonts.back();
1310
1311         fontDescription.path = std::move( path );
1312
1313         int width = 0;
1314         int weight = 0;
1315         int slant = 0;
1316         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
1317         GetFcInt( fontPattern, FC_WIDTH, width );
1318         GetFcInt( fontPattern, FC_WEIGHT, weight );
1319         GetFcInt( fontPattern, FC_SLANT, slant );
1320         fontDescription.width = IntToWidthType( width );
1321         fontDescription.weight = IntToWeightType( weight );
1322         fontDescription.slant = IntToSlantType( slant );
1323
1324         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  description; family : [%s]\n", fontDescription.family.c_str() );
1325         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1326         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1327         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1328         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1329       }
1330     }
1331
1332     FcFontSetDestroy( fontSet );
1333   }
1334   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::InitSystemFonts\n" );
1335 }
1336
1337 bool FontClient::Plugin::MatchFontDescriptionToPattern( FcPattern* pattern, Dali::TextAbstraction::FontDescription& fontDescription, FcCharSet** characterSet )
1338 {
1339   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1340
1341   FcResult result = FcResultMatch;
1342   FcPattern* match = FcFontMatch( nullptr /* use default configure */, pattern, &result );
1343
1344   const bool matched = nullptr != match;
1345   DALI_LOG_INFO( gLogFilter, Debug::General, "  pattern matched : %s\n", ( matched ? "true" : "false" ) );
1346
1347   if( matched )
1348   {
1349     int width = 0;
1350     int weight = 0;
1351     int slant = 0;
1352     GetFcString( match, FC_FILE, fontDescription.path );
1353     GetFcString( match, FC_FAMILY, fontDescription.family );
1354     GetFcInt( match, FC_WIDTH, width );
1355     GetFcInt( match, FC_WEIGHT, weight );
1356     GetFcInt( match, FC_SLANT, slant );
1357     fontDescription.width = IntToWidthType( width );
1358     fontDescription.weight = IntToWeightType( weight );
1359     fontDescription.slant = IntToSlantType( slant );
1360
1361     // Cache the character ranges.
1362     FcPatternGetCharSet( match, FC_CHARSET, 0u, characterSet );
1363
1364     // destroyed the matched pattern
1365     FcPatternDestroy( match );
1366
1367     DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1368     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1369     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1370     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1371     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1372   }
1373
1374   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::MatchFontDescriptionToPattern\n" );
1375   return matched;
1376 }
1377
1378 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontDescription& fontDescription ) const
1379 {
1380   // create the cached font family lookup pattern
1381   // a pattern holds a set of names, each name refers to a property of the font
1382   FcPattern* fontFamilyPattern = FcPatternCreate();
1383
1384   if( !fontFamilyPattern )
1385   {
1386     return nullptr;
1387   }
1388
1389   // add a property to the pattern for the font family
1390   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontDescription.family.c_str() ) );
1391
1392   int width = FONT_WIDTH_TYPE_TO_INT[fontDescription.width];
1393   if( width < 0 )
1394   {
1395     // Use default.
1396     width = DEFAULT_FONT_WIDTH;
1397   }
1398
1399   int weight = FONT_WEIGHT_TYPE_TO_INT[fontDescription.weight];
1400   if( weight < 0 )
1401   {
1402     // Use default.
1403     weight = DEFAULT_FONT_WEIGHT;
1404   }
1405
1406   int slant = FONT_SLANT_TYPE_TO_INT[fontDescription.slant];
1407   if( slant < 0 )
1408   {
1409     // Use default.
1410     slant = DEFAULT_FONT_SLANT;
1411   }
1412
1413   FcPatternAddInteger( fontFamilyPattern, FC_WIDTH, width );
1414   FcPatternAddInteger( fontFamilyPattern, FC_WEIGHT, weight );
1415   FcPatternAddInteger( fontFamilyPattern, FC_SLANT, slant );
1416
1417   // Add a property of the pattern, to say we want to match TrueType fonts
1418   FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
1419
1420   // modify the config, with the mFontFamilyPatterm
1421   FcConfigSubstitute( nullptr /* use default configure */, fontFamilyPattern, FcMatchPattern );
1422
1423   // provide default values for unspecified properties in the font pattern
1424   // e.g. patterns without a specified style or weight are set to Medium
1425   FcDefaultSubstitute( fontFamilyPattern );
1426
1427   return fontFamilyPattern;
1428 }
1429
1430 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
1431 {
1432   // create a new pattern.
1433   // a pattern holds a set of names, each name refers to a property of the font
1434   FcPattern* pattern = FcPatternCreate();
1435
1436   FcFontSet* fontset = NULL;
1437
1438   // create an object set used to define which properties are to be returned in the patterns from FcFontList.
1439   FcObjectSet* objectSet = FcObjectSetCreate();
1440
1441   if( objectSet )
1442   {
1443     // build an object set from a list of property names
1444     FcObjectSetAdd( objectSet, FC_FILE );
1445     FcObjectSetAdd( objectSet, FC_FAMILY );
1446     FcObjectSetAdd( objectSet, FC_WIDTH );
1447     FcObjectSetAdd( objectSet, FC_WEIGHT );
1448     FcObjectSetAdd( objectSet, FC_SLANT );
1449
1450     // get a list of fonts
1451     // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
1452     fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
1453
1454     // clear up the object set
1455     FcObjectSetDestroy( objectSet );
1456   }
1457   // clear up the pattern
1458   if( pattern )
1459   {
1460     FcPatternDestroy( pattern );
1461   }
1462
1463   return fontset;
1464 }
1465
1466 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
1467                                       const char* const n,
1468                                       std::string& string )
1469 {
1470   FcChar8* file = nullptr;
1471   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
1472
1473   if( FcResultMatch == retVal )
1474   {
1475     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
1476     string.assign( reinterpret_cast<const char*>( file ) );
1477
1478     return true;
1479   }
1480
1481   return false;
1482 }
1483
1484 bool FontClient::Plugin::GetFcInt( const _FcPattern* const pattern, const char* const n, int& intVal )
1485 {
1486   const FcResult retVal = FcPatternGetInteger( pattern, n, 0u, &intVal );
1487
1488   if( FcResultMatch == retVal )
1489   {
1490     return true;
1491   }
1492
1493   return false;
1494 }
1495
1496 FontId FontClient::Plugin::CreateFont( const FontPath& path,
1497                                        PointSize26Dot6 requestedPointSize,
1498                                        FaceIndex faceIndex,
1499                                        bool cacheDescription )
1500 {
1501   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::CreateFont\n" );
1502   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
1503   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1504
1505   FontId id = 0u;
1506
1507   // Create & cache new font face
1508   FT_Face ftFace;
1509   int error = FT_New_Face( mFreeTypeLibrary,
1510                            path.c_str(),
1511                            0,
1512                            &ftFace );
1513
1514   if( FT_Err_Ok == error )
1515   {
1516     // Check if a font is scalable.
1517     const bool isScalable = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_SCALABLE ) );
1518     const bool hasFixedSizedBitmaps = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_FIXED_SIZES ) ) && ( 0 != ftFace->num_fixed_sizes );
1519     const bool hasColorTables = ( 0 != ( ftFace->face_flags & FT_FACE_FLAG_COLOR ) );
1520
1521     DALI_LOG_INFO( gLogFilter, Debug::General, "            isScalable : [%s]\n", ( isScalable ? "true" : "false" ) );
1522     DALI_LOG_INFO( gLogFilter, Debug::General, "  hasFixedSizedBitmaps : [%s]\n", ( hasFixedSizedBitmaps ? "true" : "false" ) );
1523     DALI_LOG_INFO( gLogFilter, Debug::General, "        hasColorTables : [%s]\n", ( hasColorTables ? "true" : "false" ) );
1524
1525     // Check to see if the font contains fixed sizes?
1526     if( !isScalable && hasFixedSizedBitmaps )
1527     {
1528       PointSize26Dot6 actualPointSize = 0u;
1529       int fixedSizeIndex = 0;
1530       for( ; fixedSizeIndex < ftFace->num_fixed_sizes; ++fixedSizeIndex )
1531       {
1532         const PointSize26Dot6 fixedSize = ftFace->available_sizes[fixedSizeIndex].size;
1533         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, size : %d\n", fixedSizeIndex, fixedSize );
1534
1535         if( fixedSize >= requestedPointSize )
1536         {
1537           actualPointSize = fixedSize;
1538           break;
1539         }
1540       }
1541
1542       if( 0u == actualPointSize )
1543       {
1544         // The requested point size is bigger than the bigest fixed size.
1545         fixedSizeIndex = ftFace->num_fixed_sizes - 1;
1546         actualPointSize = ftFace->available_sizes[fixedSizeIndex].size;
1547       }
1548
1549       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  size index : %d, actual size : %d\n", fixedSizeIndex, actualPointSize );
1550
1551       // Tell Freetype to use this size
1552       error = FT_Select_Size( ftFace, fixedSizeIndex );
1553       if ( FT_Err_Ok != error )
1554       {
1555         DALI_LOG_INFO( gLogFilter, Debug::General, "FreeType Select_Size error: %d\n", error );
1556       }
1557       else
1558       {
1559         float fixedWidth  = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].width );
1560         float fixedHeight = static_cast< float >( ftFace->available_sizes[ fixedSizeIndex ].height );
1561
1562         // Indicate that the font is a fixed sized bitmap
1563         FontMetrics metrics( fixedHeight, // The ascender in pixels.
1564                              0.0f,
1565                              fixedHeight, // The height in pixels.
1566                              0.0f,
1567                              0.0f );
1568
1569         mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics, fixedWidth, fixedHeight, hasColorTables ) );
1570         id = mFontFaceCache.size();
1571       }
1572     }
1573     else
1574     {
1575       error = FT_Set_Char_Size( ftFace,
1576                                 0,
1577                                 requestedPointSize,
1578                                 mDpiHorizontal,
1579                                 mDpiVertical );
1580
1581       if( FT_Err_Ok == error )
1582       {
1583
1584         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
1585
1586         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
1587                              static_cast< float >( ftMetrics.descender ) * FROM_266,
1588                              static_cast< float >( ftMetrics.height    ) * FROM_266,
1589                              static_cast< float >( ftFace->underline_position ) * FROM_266,
1590                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
1591
1592         mFontFaceCache.push_back( FontFaceCacheItem( ftFace, path, requestedPointSize, faceIndex, metrics ) );
1593         id = mFontFaceCache.size();
1594       }
1595       else
1596       {
1597         DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType Set_Char_Size error: %d for pointSize %d\n", error, requestedPointSize );
1598       }
1599     }
1600
1601     if( 0u != id )
1602     {
1603       if( cacheDescription )
1604       {
1605         CacheFontPath( ftFace, id, requestedPointSize, path );
1606       }
1607     }
1608   }
1609   else
1610   {
1611     DALI_LOG_INFO( gLogFilter, Debug::General, "  FreeType New_Face error: %d for [%s]\n", error, path.c_str() );
1612   }
1613
1614   DALI_LOG_INFO( gLogFilter, Debug::General, "  font id : %d\n", id );
1615   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::CreateFont\n" );
1616
1617   return id;
1618 }
1619
1620 void FontClient::Plugin::ConvertBitmap( TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap srcBitmap )
1621 {
1622   if( srcBitmap.width*srcBitmap.rows > 0 )
1623   {
1624     switch( srcBitmap.pixel_mode )
1625     {
1626       case FT_PIXEL_MODE_GRAY:
1627       {
1628         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width ) )
1629         {
1630           const unsigned int bufferSize = srcBitmap.width * srcBitmap.rows;
1631           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1632           data.width = srcBitmap.width;
1633           data.height = srcBitmap.rows;
1634           data.format = Pixel::L8;
1635           memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1636         }
1637         break;
1638       }
1639
1640 #ifdef FREETYPE_BITMAP_SUPPORT
1641       case FT_PIXEL_MODE_BGRA:
1642       {
1643         if( srcBitmap.pitch == static_cast<int>( srcBitmap.width << 2u ) )
1644         {
1645           // Set the input dimensions.
1646           const ImageDimensions inputDimensions( srcBitmap.width, srcBitmap.rows );
1647
1648           // Set the output dimensions.
1649           // If the output dimension is not given, the input dimension is set
1650           // and won't be downscaling.
1651           data.width = ( data.width == 0 ) ? srcBitmap.width : data.width;
1652           data.height = ( data.height == 0 ) ? srcBitmap.rows : data.height;
1653           const ImageDimensions desiredDimensions( data.width, data.height );
1654
1655           // Creates the output buffer
1656           const unsigned int bufferSize = data.width * data.height * 4u;
1657           data.buffer = new unsigned char[bufferSize]; // @note The caller is responsible for deallocating the bitmap data using delete[].
1658
1659           if( inputDimensions == desiredDimensions )
1660           {
1661             // There isn't downscaling.
1662             memcpy( data.buffer, srcBitmap.buffer, bufferSize );
1663           }
1664           else
1665           {
1666             Dali::Internal::Platform::LanczosSample4BPP( srcBitmap.buffer,
1667                                                          inputDimensions,
1668                                                          data.buffer,
1669                                                          desiredDimensions );
1670           }
1671           data.format = Pixel::BGRA8888;
1672         }
1673         break;
1674       }
1675 #endif
1676       default:
1677       {
1678         DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::ConvertBitmap. FontClient Unable to create Bitmap of this PixelType\n" );
1679         break;
1680       }
1681     }
1682   }
1683 }
1684
1685 bool FontClient::Plugin::FindFont( const FontPath& path,
1686                                    PointSize26Dot6 requestedPointSize,
1687                                    FaceIndex faceIndex,
1688                                    FontId& fontId ) const
1689 {
1690   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
1691   DALI_LOG_INFO( gLogFilter, Debug::General, "                path : [%s]\n", path.c_str() );
1692   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1693   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fonts in the cache : %d\n", mFontFaceCache.size() );
1694
1695   fontId = 0u;
1696   for( const auto& cacheItem : mFontFaceCache )
1697   {
1698     ++fontId;
1699     if( cacheItem.mRequestedPointSize == requestedPointSize &&
1700         cacheItem.mFaceIndex == faceIndex &&
1701         cacheItem.mPath == path )
1702     {
1703
1704       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
1705       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1706
1707       return true;
1708     }
1709   }
1710
1711   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found\n" );
1712   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1713
1714   fontId = 0u;
1715   return false;
1716 }
1717
1718 bool FontClient::Plugin::FindValidatedFont( const FontDescription& fontDescription,
1719                                             FontDescriptionId& validatedFontId )
1720 {
1721   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindValidatedFont\n" );
1722   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1723   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1724   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1725   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1726   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1727   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of validated fonts in the cache : %d\n", mValidatedFontCache.size() );
1728
1729   validatedFontId = 0u;
1730
1731   for( const auto& item : mValidatedFontCache )
1732   {
1733     if( !fontDescription.family.empty() &&
1734         ( fontDescription.family == item.fontDescription.family ) &&
1735         ( fontDescription.width == item.fontDescription.width ) &&
1736         ( fontDescription.weight == item.fontDescription.weight ) &&
1737         ( fontDescription.slant == item.fontDescription.slant ) )
1738     {
1739       validatedFontId = item.index;
1740
1741       DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font found, id : %d\n", validatedFontId );
1742       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
1743       return true;
1744     }
1745   }
1746
1747   DALI_LOG_INFO( gLogFilter, Debug::General, "  validated font not found\n" );
1748   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindValidatedFont\n" );
1749   return false;
1750 }
1751
1752 bool FontClient::Plugin::FindFallbackFontList( const FontDescription& fontDescription,
1753                                                FontList*& fontList,
1754                                                CharacterSetList*& characterSetList )
1755 {
1756   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFallbackFontList\n" );
1757   DALI_LOG_INFO( gLogFilter, Debug::General, "  description; family : [%s]\n", fontDescription.family.c_str() );
1758   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                 path : [%s]\n", fontDescription.path.c_str() );
1759   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                width : [%s]\n", FontWidth::Name[fontDescription.width] );
1760   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "               weight : [%s]\n", FontWeight::Name[fontDescription.weight] );
1761   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                slant : [%s]\n\n", FontSlant::Name[fontDescription.slant] );
1762   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of fallback font lists in the cache : %d\n", mFallbackCache.size() );
1763
1764   fontList = nullptr;
1765
1766   for( const auto& item : mFallbackCache )
1767   {
1768     if( !fontDescription.family.empty() &&
1769         ( fontDescription.family == item.fontDescription.family ) &&
1770         ( fontDescription.width == item.fontDescription.width ) &&
1771         ( fontDescription.weight == item.fontDescription.weight ) &&
1772         ( fontDescription.slant == item.fontDescription.slant ) )
1773     {
1774       fontList = item.fallbackFonts;
1775       characterSetList = item.characterSets;
1776
1777       DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list found.\n" );
1778       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
1779       return true;
1780     }
1781   }
1782
1783   DALI_LOG_INFO( gLogFilter, Debug::General, "  fallback font list not found.\n" );
1784   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFallbackFontList\n" );
1785   return false;
1786 }
1787
1788 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1789                                    PointSize26Dot6 requestedPointSize,
1790                                    FontId& fontId )
1791 {
1792   DALI_LOG_INFO( gLogFilter, Debug::General, "-->FontClient::Plugin::FindFont\n" );
1793   DALI_LOG_INFO( gLogFilter, Debug::General, "    validatedFontId  : %d\n", validatedFontId );
1794   DALI_LOG_INFO( gLogFilter, Debug::General, "  requestedPointSize : %d\n", requestedPointSize );
1795
1796   fontId = 0u;
1797
1798   for( const auto& item : mFontDescriptionSizeCache )
1799   {
1800     if( ( validatedFontId == item.validatedFontId ) &&
1801         ( requestedPointSize == item.requestedPointSize ) )
1802     {
1803       fontId = item.fontId;
1804
1805       DALI_LOG_INFO( gLogFilter, Debug::General, "  font found, id : %d\n", fontId );
1806       DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1807       return true;
1808     }
1809   }
1810
1811   DALI_LOG_INFO( gLogFilter, Debug::General, "  font not found.\n" );
1812   DALI_LOG_INFO( gLogFilter, Debug::General, "<--FontClient::Plugin::FindFont\n" );
1813   return false;
1814 }
1815
1816 bool FontClient::Plugin::IsScalable( const FontPath& path )
1817 {
1818   bool isScalable = false;
1819
1820   FT_Face ftFace;
1821   int error = FT_New_Face( mFreeTypeLibrary,
1822                            path.c_str(),
1823                            0,
1824                            &ftFace );
1825   if( FT_Err_Ok != error )
1826   {
1827     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: %s\n", path.c_str() );
1828   }
1829   else
1830   {
1831     isScalable = ftFace->face_flags & FT_FACE_FLAG_SCALABLE;
1832   }
1833
1834   return isScalable;
1835 }
1836
1837 bool FontClient::Plugin::IsScalable( const FontDescription& fontDescription )
1838 {
1839   // Create a font pattern.
1840   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1841
1842   FcResult result = FcResultMatch;
1843
1844   // match the pattern
1845   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
1846   bool isScalable = false;
1847
1848   if( match )
1849   {
1850     // Get the path to the font file name.
1851     FontPath path;
1852     GetFcString( match, FC_FILE, path );
1853     isScalable = IsScalable( path );
1854   }
1855   else
1856   {
1857     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::IsScalable. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
1858   }
1859   FcPatternDestroy( fontFamilyPattern );
1860   FcPatternDestroy( match );
1861   return isScalable;
1862 }
1863
1864 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1865 {
1866   // Empty the caller container
1867   sizes.Clear();
1868
1869   FT_Face ftFace;
1870   int error = FT_New_Face( mFreeTypeLibrary,
1871                            path.c_str(),
1872                            0,
1873                            &ftFace );
1874   if( FT_Err_Ok != error )
1875   {
1876     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font path : [%s]\n", path.c_str() );
1877   }
1878
1879   // Fetch the number of fixed sizes available
1880   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1881   {
1882     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1883     {
1884       sizes.PushBack( ftFace->available_sizes[ i ].size );
1885     }
1886   }
1887 }
1888
1889 void FontClient::Plugin::GetFixedSizes( const FontDescription& fontDescription,
1890                                         Vector< PointSize26Dot6 >& sizes )
1891 {
1892   // Create a font pattern.
1893   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontDescription );
1894
1895   FcResult result = FcResultMatch;
1896
1897   // match the pattern
1898   FcPattern* match = FcFontMatch( nullptr /* use default configure */, fontFamilyPattern, &result );
1899
1900   if( match )
1901   {
1902     // Get the path to the font file name.
1903     FontPath path;
1904     GetFcString( match, FC_FILE, path );
1905     GetFixedSizes( path, sizes );
1906   }
1907   else
1908   {
1909     DALI_LOG_INFO( gLogFilter, Debug::General, "FontClient::Plugin::GetFixedSizes. FreeType Cannot check font: [%s]\n", fontDescription.family.c_str() );
1910   }
1911   FcPatternDestroy( match );
1912   FcPatternDestroy( fontFamilyPattern );
1913 }
1914
1915 void FontClient::Plugin::CacheFontPath( FT_Face ftFace, FontId id, PointSize26Dot6 requestedPointSize,  const FontPath& path )
1916 {
1917   FontDescription description;
1918   description.path = path;
1919   description.family = std::move( FontFamily( ftFace->family_name ) );
1920   description.weight = FontWeight::NONE;
1921   description.width = FontWidth::NONE;
1922   description.slant = FontSlant::NONE;
1923
1924   // Note FreeType doesn't give too much info to build a proper font style.
1925   if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC )
1926   {
1927     description.slant = FontSlant::ITALIC;
1928   }
1929   if( ftFace->style_flags & FT_STYLE_FLAG_BOLD )
1930   {
1931     description.weight = FontWeight::BOLD;
1932   }
1933
1934   FontDescriptionId validatedFontId = 0u;
1935   if( !FindValidatedFont( description,
1936                           validatedFontId ) )
1937   {
1938     // Set the index to the vector of paths to font file names.
1939     validatedFontId = mFontDescriptionCache.size();
1940
1941     FcPattern* pattern = CreateFontFamilyPattern( description );
1942
1943     FcResult result = FcResultMatch;
1944     FcPattern* match = FcFontMatch( nullptr, pattern, &result );
1945
1946     FcCharSet* characterSet = nullptr;
1947     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
1948
1949     FcPatternDestroy( pattern );
1950
1951     mMatchedFcPatternCache.PushBack( match );
1952
1953     mFontFaceCache[id-1u].mCharacterSet = characterSet;
1954
1955     // Add the path to the cache.
1956     mFontDescriptionCache.push_back( description );
1957     mCharacterSetCache.PushBack( characterSet );
1958
1959     // Cache the index and the font's description.
1960     mValidatedFontCache.push_back( std::move( FontDescriptionCacheItem( std::move( description ),
1961                                                                         validatedFontId) ) );
1962
1963     // Cache the pair 'validatedFontId, requestedPointSize' to improve the following queries.
1964     mFontDescriptionSizeCache.push_back( FontDescriptionSizeCacheItem( validatedFontId,
1965                                                                        requestedPointSize,
1966                                                                        id ) );
1967   }
1968 }
1969
1970 FcCharSet* FontClient::Plugin::CreateCharacterSetFromDescription( const FontDescription& description )
1971 {
1972   FcCharSet* characterSet = nullptr;
1973
1974   FcPattern* pattern = CreateFontFamilyPattern( description );
1975
1976   if( nullptr != pattern )
1977   {
1978     FcResult result = FcResultMatch;
1979     FcPattern* match = FcFontMatch( nullptr, pattern, &result );
1980
1981     FcPatternGetCharSet( match, FC_CHARSET, 0u, &characterSet );
1982     mMatchedFcPatternCache.PushBack( match );
1983
1984     FcPatternDestroy( pattern );
1985   }
1986
1987   return characterSet;
1988 }
1989
1990 void FontClient::Plugin::DestroyMatchedPatterns()
1991 {
1992   for (auto & object : mMatchedFcPatternCache)
1993   {
1994     FcPatternDestroy(reinterpret_cast<FcPattern*>(object));
1995   }
1996   mMatchedFcPatternCache.Clear();
1997 }
1998
1999 } // namespace Internal
2000
2001 } // namespace TextAbstraction
2002
2003 } // namespace Dali