Scaling feature for fixed-size fonts
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / font-client-plugin-impl.cpp
1 /*
2  * Copyright (c) 2015 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-abstraction/font-client-plugin-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/common/dali-vector.h>
23 #include <dali/public-api/common/vector-wrapper.h>
24 #include <dali/integration-api/debug.h>
25
26 // EXTERNAL INCLUDES
27 #include <fontconfig/fontconfig.h>
28
29 /**
30  * Conversion from Fractional26.6 to float
31  */
32 namespace
33 {
34 const float FROM_266 = 1.0f / 64.0f;
35
36 const std::string FONT_FORMAT( "TrueType" );
37 const std::string DEFAULT_FONT_FAMILY_NAME( "Tizen" );
38 const std::string DEFAULT_FONT_STYLE( "Regular" );
39
40 const uint32_t ELLIPSIS_CHARACTER = 0x2026;
41 }
42
43 using Dali::Vector;
44
45 namespace Dali
46 {
47
48 namespace TextAbstraction
49 {
50
51 namespace Internal
52 {
53
54 const bool FONT_FIXED_SIZE_BITMAP( true );
55
56 FontClient::Plugin::FontDescriptionCacheItem::FontDescriptionCacheItem( const FontFamily& fontFamily,
57                                                                         const FontStyle& fontStyle,
58                                                                         FontDescriptionId index )
59 : fontFamily( fontFamily ),
60   fontStyle( fontStyle ),
61   index( index )
62 {
63 }
64
65 FontClient::Plugin::FontIdCacheItem::FontIdCacheItem( FontDescriptionId validatedFontId,
66                                                       PointSize26Dot6 pointSize,
67                                                       FontId fontId )
68 : validatedFontId( validatedFontId ),
69   pointSize( pointSize ),
70   fontId( fontId )
71 {
72 }
73
74 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
75                                           const FontPath& path,
76                                           PointSize26Dot6 pointSize,
77                                           FaceIndex face,
78                                           const FontMetrics& metrics )
79 : mFreeTypeFace( ftFace ),
80   mPath( path ),
81   mPointSize( pointSize ),
82   mFaceIndex( face ),
83   mMetrics( metrics ),
84   mFixedWidthPixels( 0.0f ),
85   mFixedHeightPixels( 0.0f ),
86   mIsFixedSizeBitmap( false )
87 {
88 }
89
90 FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace,
91                                           const FontPath& path,
92                                           PointSize26Dot6 pointSize,
93                                           FaceIndex face,
94                                           const FontMetrics& metrics,
95                                           float fixedWidth,
96                                           float fixedHeight )
97 : mFreeTypeFace( ftFace ),
98   mPath( path ),
99   mPointSize( pointSize ),
100   mFaceIndex( face ),
101   mMetrics( metrics ),
102   mFixedWidthPixels( fixedWidth ),
103   mFixedHeightPixels( fixedHeight ),
104   mIsFixedSizeBitmap( true )
105 {
106 }
107
108 FontClient::Plugin::Plugin( unsigned int horizontalDpi,
109                             unsigned int verticalDpi )
110 : mFreeTypeLibrary( NULL ),
111   mDpiHorizontal( horizontalDpi ),
112   mDpiVertical( verticalDpi ),
113   mSystemFonts(),
114   mDefaultFonts(),
115   mFontCache(),
116   mValidatedFontCache(),
117   mFontDescriptionCache( 1u ),
118   mFontIdCache(),
119   mEllipsisCache()
120 {
121   int error = FT_Init_FreeType( &mFreeTypeLibrary );
122   if( FT_Err_Ok != error )
123   {
124     DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
125   }
126 }
127
128 FontClient::Plugin::~Plugin()
129 {
130   FT_Done_FreeType( mFreeTypeLibrary );
131 }
132
133 void FontClient::Plugin::SetDpi( unsigned int horizontalDpi,
134                                  unsigned int verticalDpi )
135 {
136   mDpiHorizontal = horizontalDpi;
137   mDpiVertical = verticalDpi;
138 }
139
140 void FontClient::Plugin::SetDefaultFontFamily( const FontFamily& fontFamilyName,
141                                                const FontStyle& fontStyle )
142 {
143   mDefaultFonts.clear();
144
145   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamilyName,
146                                                           fontStyle );
147
148   FcResult result = FcResultMatch;
149
150   // Match the pattern.
151   FcFontSet* fontSet = FcFontSort( NULL /* use default configure */,
152                                    fontFamilyPattern,
153                                    false /* don't trim */,
154                                    NULL,
155                                    &result );
156
157   if( NULL != fontSet )
158   {
159     // Reserve some space to avoid reallocations.
160     mDefaultFonts.reserve( fontSet->nfont );
161
162     for( int i = 0u; i < fontSet->nfont; ++i )
163     {
164       FcPattern* fontPattern = fontSet->fonts[i];
165
166       FontPath path;
167
168       // Skip fonts with no path
169       if( GetFcString( fontPattern, FC_FILE, path ) )
170       {
171         mDefaultFonts.push_back( FontDescription() );
172         FontDescription& fontDescription = mDefaultFonts.back();
173
174         fontDescription.path = path;
175
176         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
177         GetFcString( fontPattern, FC_STYLE, fontDescription.style );
178       }
179     }
180
181     FcFontSetDestroy( fontSet );
182   }
183
184   FcPatternDestroy( fontFamilyPattern );
185 }
186
187 void FontClient::Plugin::GetDefaultFonts( FontList& defaultFonts )
188 {
189   if( mDefaultFonts.empty() )
190   {
191     SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME,
192                           DEFAULT_FONT_STYLE );
193   }
194
195   defaultFonts = mDefaultFonts;
196 }
197
198 void FontClient::Plugin::GetSystemFonts( FontList& systemFonts )
199 {
200   if( mSystemFonts.empty() )
201   {
202     InitSystemFonts();
203   }
204
205   systemFonts = mSystemFonts;
206 }
207
208 void FontClient::Plugin::GetDescription( FontId id,
209                                          FontDescription& fontDescription ) const
210 {
211   for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
212          endIt = mFontIdCache.end();
213        it != endIt;
214        ++it )
215   {
216     const FontIdCacheItem& item = *it;
217
218     if( item.fontId == id )
219     {
220       fontDescription = *( mFontDescriptionCache.begin() + item.validatedFontId );
221       return;
222     }
223   }
224
225   DALI_LOG_ERROR( "FontClient::Plugin::GetDescription. No description found for the font ID %d\n", id );
226 }
227
228 PointSize26Dot6 FontClient::Plugin::GetPointSize( FontId id )
229 {
230   const FontId index = id - 1u;
231
232   if( id > 0u &&
233       index < mFontCache.size() )
234   {
235     return ( *( mFontCache.begin() + index ) ).mPointSize;
236   }
237   else
238   {
239     DALI_LOG_ERROR( "FontClient::Plugin::GetPointSize. Invalid font ID %d\n", id );
240   }
241
242   return TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
243 }
244
245 FontId FontClient::Plugin::FindDefaultFont( Character charcode,
246                                             PointSize26Dot6 requestedSize,
247                                             bool preferColor )
248 {
249   FontId fontId(0);
250   bool foundColor(false);
251
252   // Create the list of default fonts if it has not been created.
253   if( mDefaultFonts.empty() )
254   {
255     SetDefaultFontFamily( DEFAULT_FONT_FAMILY_NAME,
256                           DEFAULT_FONT_STYLE );
257   }
258
259   // Traverse the list of default fonts.
260   // Check for each default font if supports the character.
261
262   for( FontList::const_iterator it = mDefaultFonts.begin(), endIt = mDefaultFonts.end();
263        it != endIt;
264        ++it )
265   {
266     const FontDescription& description = *it;
267
268     FcPattern* pattern = CreateFontFamilyPattern( description.family,
269                                                   description.style );
270
271     FcResult result = FcResultMatch;
272     FcPattern* match = FcFontMatch( NULL /* use default configure */, pattern, &result );
273
274     FcCharSet* charSet = NULL;
275     FcPatternGetCharSet( match, FC_CHARSET, 0u, &charSet );
276
277     if( FcCharSetHasChar( charSet, charcode ) )
278     {
279       Vector< PointSize26Dot6 > fixedSizes;
280       GetFixedSizes( description.family,
281                      description.style,
282                      fixedSizes );
283
284       const Vector< PointSize26Dot6 >::SizeType count = fixedSizes.Count();
285       if( 0 != count )
286       {
287         // If the font is not scalable, pick the largest size <= requestedSize
288         PointSize26Dot6 size = fixedSizes[0];
289         for( unsigned int i=1; i<count; ++i )
290         {
291           if( fixedSizes[i] <= requestedSize &&
292               fixedSizes[i] > size )
293           {
294             size = fixedSizes[i];
295           }
296         }
297         requestedSize = size;
298       }
299
300       fontId = GetFontId( description.family,
301                           description.style,
302                           requestedSize,
303                           0u );
304
305       if( preferColor )
306       {
307         BufferImage bitmap = CreateBitmap( fontId, GetGlyphIndex(fontId,charcode) );
308         if( bitmap &&
309             Pixel::BGRA8888 == bitmap.GetPixelFormat() )
310         {
311           foundColor = true;
312         }
313       }
314
315       // Keep going unless we prefer a different (color) font
316       if( !preferColor || foundColor )
317       {
318         FcPatternDestroy( match );
319         FcPatternDestroy( pattern );
320         break;
321       }
322     }
323
324     FcPatternDestroy( match );
325     FcPatternDestroy( pattern );
326   }
327   return fontId;
328 }
329
330 FontId FontClient::Plugin::GetFontId( const FontPath& path,
331                                       PointSize26Dot6 pointSize,
332                                       FaceIndex faceIndex,
333                                       bool cacheDescription )
334 {
335   FontId id( 0 );
336
337   if( NULL != mFreeTypeLibrary )
338   {
339     FontId foundId(0);
340     if( FindFont( path, pointSize, faceIndex, foundId ) )
341     {
342       id = foundId;
343     }
344     else
345     {
346       id = CreateFont( path, pointSize, faceIndex, cacheDescription );
347     }
348   }
349
350   return id;
351 }
352
353 FontId FontClient::Plugin::GetFontId( const FontFamily& fontFamily,
354                                       const FontStyle& fontStyle,
355                                       PointSize26Dot6 pointSize,
356                                       FaceIndex faceIndex )
357 {
358   // This method uses three vectors which caches:
359   // * Pairs of non validated 'fontFamily, fontStyle' and an index to a vector with paths to font file names.
360   // * The path to font file names.
361   // * The font ids of pairs 'font point size, index to the vector with paths to font file names'.
362
363   // 1) Checks in the cache if the pair 'fontFamily, fontStyle' has been validated before.
364   //    If it was it gets an index to the vector with paths to font file names. Otherwise,
365   //    retrieves using font config a path to a font file name which matches with the pair
366   //    'fontFamily, fontStyle'. The path is stored in the chache.
367   //
368   // 2) Checks in the cache if the pair 'font point size, index to the vector with paths to
369   //    fon file names' exists. If exists, it gets the font id. If it doesn't it calls
370   //    the GetFontId() method with the path to the font file name and the point size to
371   //    get the font id.
372
373   // The font id to be returned.
374   FontId fontId = 0u;
375
376   // Check first if the pair font family and style have been validated before.
377   FontDescriptionId validatedFontId = 0u;
378
379   if( !FindValidatedFont( fontFamily,
380                           fontStyle,
381                           validatedFontId ) )
382   {
383     // Use font config to validate the font family name and font style.
384     ValidateFont( fontFamily, fontStyle, validatedFontId );
385   }
386
387   // Check if exists a pair 'validatedFontId, pointSize' in the cache.
388   if( !FindFont( validatedFontId, pointSize, fontId ) )
389   {
390     // Retrieve the font file name path.
391     const FontDescription& description = *( mFontDescriptionCache.begin() + validatedFontId );
392
393     // Retrieve the font id. Do not cache the description as it has been already cached.
394     fontId = GetFontId( description.path,
395                         pointSize,
396                         faceIndex,
397                         false );
398
399     // Cache the pair 'validatedFontId, pointSize' to improve the following queries.
400     mFontIdCache.push_back( FontIdCacheItem( validatedFontId,
401                                              pointSize,
402                                              fontId ) );
403   }
404
405   return fontId;
406 }
407
408 void FontClient::Plugin::ValidateFont( const FontFamily& fontFamily,
409                                        const FontStyle& fontStyle,
410                                        FontDescriptionId& validatedFontId )
411 {
412   // Create a font pattern.
413   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamily,
414                                                             fontStyle );
415
416   FcResult result = FcResultMatch;
417
418   // match the pattern
419   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
420
421   if( match )
422   {
423     // Get the path to the font file name.
424     FontDescription description;
425     GetFcString( match, FC_FILE, description.path );
426     GetFcString( match, FC_FAMILY, description.family );
427     GetFcString( match, FC_STYLE, description.style );
428
429     // Set the index to the vector of paths to font file names.
430     validatedFontId = mFontDescriptionCache.size();
431
432     // Add the path to the cache.
433     mFontDescriptionCache.push_back( description );
434
435     // Cache the index and the pair font family name, font style.
436     FontDescriptionCacheItem item( fontFamily, fontStyle, validatedFontId );
437     mValidatedFontCache.push_back( item );
438
439     // destroyed the matched pattern
440     FcPatternDestroy( match );
441   }
442   else
443   {
444     DALI_LOG_ERROR( "FontClient::Plugin::ValidateFont failed for font %s %s\n", fontFamily.c_str(), fontStyle.c_str() );
445   }
446
447   // destroy the pattern
448   FcPatternDestroy( fontFamilyPattern );
449 }
450
451 void FontClient::Plugin::GetFontMetrics( FontId fontId,
452                                          FontMetrics& metrics,
453                                          int maxFixedSize )
454 {
455   if( fontId > 0 &&
456       fontId-1 < mFontCache.size() )
457   {
458     const CacheItem& font = mFontCache[fontId-1];
459
460     metrics = font.mMetrics;
461
462     // Adjust the metrics if the fixed-size font should be down-scaled
463     if( font.mIsFixedSizeBitmap &&
464         ( maxFixedSize > 0 ) &&
465         ( font.mFixedHeightPixels > maxFixedSize ) )
466     {
467       float scaleFactor = static_cast<float>(maxFixedSize) / static_cast<float>(font.mFixedHeightPixels);
468
469       metrics.ascender           *= scaleFactor;
470       metrics.descender          *= scaleFactor;
471       metrics.height             *= scaleFactor;
472       metrics.underlinePosition  *= scaleFactor;
473       metrics.underlineThickness *= scaleFactor;
474     }
475   }
476   else
477   {
478     DALI_LOG_ERROR( "Invalid font ID %d\n", fontId );
479   }
480 }
481
482 GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId,
483                                               Character charcode )
484 {
485   GlyphIndex index( 0 );
486
487   if( fontId > 0 &&
488       fontId-1 < mFontCache.size() )
489   {
490     FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
491
492     index = FT_Get_Char_Index( ftFace, charcode );
493   }
494
495   return index;
496 }
497
498 bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array,
499                                           uint32_t size,
500                                           bool horizontal,
501                                           int maxFixedSize )
502 {
503   bool success( true );
504
505   for( unsigned int i=0; i<size; ++i )
506   {
507     FontId fontId = array[i].fontId;
508
509     if( fontId > 0 &&
510         fontId-1 < mFontCache.size() )
511     {
512       const CacheItem& font = mFontCache[fontId-1];
513
514       FT_Face ftFace = font.mFreeTypeFace;
515
516 #ifdef FREETYPE_BITMAP_SUPPORT
517       // Check to see if we should be loading a Fixed Size bitmap?
518       if ( font.mIsFixedSizeBitmap )
519       {
520         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_COLOR );
521         if ( FT_Err_Ok == error )
522         {
523           array[i].width = font.mFixedWidthPixels;
524           array[i].height = font.mFixedHeightPixels;
525           array[i].advance = font.mFixedWidthPixels;
526           array[i].xBearing = 0.0f;
527           array[i].yBearing = font.mFixedHeightPixels;
528
529           // Adjust the metrics if the fixed-size font should be down-scaled
530           if( ( maxFixedSize > 0 ) &&
531               ( font.mFixedHeightPixels > maxFixedSize ) )
532           {
533             float scaleFactor = static_cast<float>(maxFixedSize) / static_cast<float>(font.mFixedHeightPixels);
534
535             array[i].width    *= scaleFactor;
536             array[i].height   *= scaleFactor;
537             array[i].advance  *= scaleFactor;
538             array[i].xBearing *= scaleFactor;
539             array[i].yBearing *= scaleFactor;
540
541             array[i].scaleFactor = scaleFactor;
542           }
543         }
544         else
545         {
546           DALI_LOG_ERROR( "FreeType Bitmap Load_Glyph error %d\n", error );
547           success = false;
548         }
549       }
550       else
551 #endif
552       {
553         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
554
555         if( FT_Err_Ok == error )
556         {
557           array[i].width  = static_cast< float >( ftFace->glyph->metrics.width ) * FROM_266;
558           array[i].height = static_cast< float >( ftFace->glyph->metrics.height ) * FROM_266 ;
559           if( horizontal )
560           {
561             array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingX ) * FROM_266;
562             array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.horiBearingY ) * FROM_266;
563           }
564           else
565           {
566             array[i].xBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingX ) * FROM_266;
567             array[i].yBearing += static_cast< float >( ftFace->glyph->metrics.vertBearingY ) * FROM_266;
568           }
569         }
570         else
571         {
572           success = false;
573         }
574       }
575     }
576     else
577     {
578       success = false;
579     }
580   }
581
582   return success;
583 }
584
585 BufferImage FontClient::Plugin::CreateBitmap( FontId fontId,
586                                               GlyphIndex glyphIndex )
587 {
588   BufferImage bitmap;
589
590   if( fontId > 0 &&
591       fontId-1 < mFontCache.size() )
592   {
593     FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
594
595     FT_Error error;
596
597 #ifdef FREETYPE_BITMAP_SUPPORT
598     // Check to see if this is fixed size bitmap
599     if ( mFontCache[fontId-1].mIsFixedSizeBitmap )
600     {
601       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_COLOR );
602     }
603     else
604 #endif
605     {
606       error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
607     }
608     if( FT_Err_Ok == error )
609     {
610       FT_Glyph glyph;
611       error = FT_Get_Glyph( ftFace->glyph, &glyph );
612
613       // Convert to bitmap if necessary
614       if ( FT_Err_Ok == error )
615       {
616         if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
617         {
618           error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
619           if ( FT_Err_Ok == error )
620           {
621             FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
622             ConvertBitmap( bitmap, bitmapGlyph->bitmap );
623           }
624           else
625           {
626             DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
627           }
628         }
629         else
630         {
631           ConvertBitmap( bitmap, ftFace->glyph->bitmap );
632         }
633
634         // Created FT_Glyph object must be released with FT_Done_Glyph
635         FT_Done_Glyph( glyph );
636       }
637     }
638     else
639     {
640       DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
641     }
642   }
643
644   return bitmap;
645 }
646
647 const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 pointSize )
648 {
649   // First look into the cache if there is an ellipsis glyph for the requested point size.
650   for( Vector<EllipsisItem>::ConstIterator it = mEllipsisCache.Begin(),
651          endIt = mEllipsisCache.End();
652        it != endIt;
653        ++it )
654   {
655     const EllipsisItem& item = *it;
656
657     if( fabsf( item.size - pointSize ) < Math::MACHINE_EPSILON_1000 )
658     {
659       // Use the glyph in the cache.
660       return item.glyph;
661     }
662   }
663
664   // No glyph has been found. Create one.
665   mEllipsisCache.PushBack( EllipsisItem() );
666   EllipsisItem& item = *( mEllipsisCache.End() - 1u );
667
668   item.size = pointSize;
669
670   // Find a font for the ellipsis glyph.
671   item.glyph.fontId = FindDefaultFont( ELLIPSIS_CHARACTER,
672                                        pointSize,
673                                        false );
674
675   // Set the character index to access the glyph inside the font.
676   item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace,
677                                         ELLIPSIS_CHARACTER );
678
679   GetGlyphMetrics( &item.glyph, 1u, true, 0 );
680
681   return item.glyph;
682 }
683
684 void FontClient::Plugin::InitSystemFonts()
685 {
686   FcFontSet* fontSet = GetFcFontSet();
687
688   if( fontSet )
689   {
690     // Reserve some space to avoid reallocations.
691     mSystemFonts.reserve( fontSet->nfont );
692
693     for( int i = 0u; i < fontSet->nfont; ++i )
694     {
695       FcPattern* fontPattern = fontSet->fonts[i];
696
697       FontPath path;
698
699       // Skip fonts with no path
700       if( GetFcString( fontPattern, FC_FILE, path ) )
701       {
702         mSystemFonts.push_back( FontDescription() );
703         FontDescription& fontDescription = mSystemFonts.back();
704
705         fontDescription.path = path;
706
707         GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
708         GetFcString( fontPattern, FC_STYLE, fontDescription.style );
709       }
710     }
711
712     FcFontSetDestroy( fontSet );
713   }
714 }
715
716 FcPattern* FontClient::Plugin::CreateFontFamilyPattern( const FontFamily& fontFamily,
717                                                         const FontStyle& fontStyle )
718 {
719   // create the cached font family lookup pattern
720   // a pattern holds a set of names, each name refers to a property of the font
721   FcPattern* fontFamilyPattern = FcPatternCreate();
722
723   // add a property to the pattern for the font family
724   FcPatternAddString( fontFamilyPattern, FC_FAMILY, reinterpret_cast<const FcChar8*>( fontFamily.c_str() ) );
725
726   // add a property to the pattern for the font family
727   FcPatternAddString( fontFamilyPattern, FC_STYLE, reinterpret_cast<const FcChar8*>( fontStyle.c_str() ) );
728
729   // Add a property of the pattern, to say we want to match TrueType fonts
730   FcPatternAddString( fontFamilyPattern , FC_FONTFORMAT, reinterpret_cast<const FcChar8*>( FONT_FORMAT.c_str() ) );
731
732   // modify the config, with the mFontFamilyPatterm
733   FcConfigSubstitute( NULL /* use default configure */, fontFamilyPattern, FcMatchPattern );
734
735   // provide default values for unspecified properties in the font pattern
736   // e.g. patterns without a specified style or weight are set to Medium
737   FcDefaultSubstitute( fontFamilyPattern );
738
739   return fontFamilyPattern;
740 }
741
742 _FcFontSet* FontClient::Plugin::GetFcFontSet() const
743 {
744   // create a new pattern.
745   // a pattern holds a set of names, each name refers to a property of the font
746   FcPattern* pattern = FcPatternCreate();
747
748   // create an object set used to define which properties are to be returned in the patterns from FcFontList.
749   FcObjectSet* objectSet = FcObjectSetCreate();
750
751   // build an object set from a list of property names
752   FcObjectSetAdd( objectSet, FC_FILE );
753   FcObjectSetAdd( objectSet, FC_FAMILY );
754   FcObjectSetAdd( objectSet, FC_STYLE );
755
756   // get a list of fonts
757   // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
758   FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
759
760   // clear up the object set
761   if( objectSet )
762   {
763     FcObjectSetDestroy( objectSet );
764   }
765   // clear up the pattern
766   if( pattern )
767   {
768     FcPatternDestroy( pattern );
769   }
770
771   return fontset;
772 }
773
774 bool FontClient::Plugin::GetFcString( const FcPattern* const pattern,
775                                       const char* const n,
776                                       std::string& string )
777 {
778   FcChar8* file = NULL;
779   const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
780
781   if( FcResultMatch == retVal )
782   {
783     // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
784     string.assign( reinterpret_cast<const char*>( file ) );
785
786     return true;
787   }
788
789   return false;
790 }
791
792 FontId FontClient::Plugin::CreateFont( const FontPath& path,
793                                        PointSize26Dot6 pointSize,
794                                        FaceIndex faceIndex,
795                                        bool cacheDescription )
796 {
797   FontId id( 0 );
798
799   // Create & cache new font face
800   FT_Face ftFace;
801   int error = FT_New_Face( mFreeTypeLibrary,
802                            path.c_str(),
803                            0,
804                            &ftFace );
805
806   if( FT_Err_Ok == error )
807   {
808     // Check to see if the font contains fixed sizes?
809     if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
810     {
811       // Ensure this size is available
812       for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
813       {
814         if ( static_cast<FT_Pos>(pointSize) == ftFace->available_sizes[ i ].size )
815         {
816           // Tell Freetype to use this size
817           error = FT_Select_Size( ftFace, i );
818           if ( FT_Err_Ok != error )
819           {
820             DALI_LOG_ERROR( "FreeType Select_Size error: %d\n", error );
821           }
822           else
823           {
824             float fixedWidth  = static_cast< float >( ftFace->available_sizes[ i ].width );
825             float fixedHeight = static_cast< float >( ftFace->available_sizes[ i ].height );
826
827             // Indicate that the font is a fixed sized bitmap
828             FontMetrics metrics( fixedHeight, // The ascender in pixels.
829                                  0.0f,
830                                  fixedHeight, // The height in pixels.
831                                  0.0f,
832                                  0.0f );
833
834             mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics, fixedWidth, fixedHeight ) );
835             id = mFontCache.size();
836
837             if( cacheDescription )
838             {
839               FontDescription description;
840               description.path = path;
841               description.family = FontFamily( ftFace->family_name );
842               description.style = FontStyle( ftFace->style_name );
843
844               mFontDescriptionCache.push_back( description );
845             }
846             return id;
847           }
848         }
849       }
850
851       // Can't find this size
852       std::stringstream sizes;
853       for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
854       {
855         if ( i )
856         {
857           sizes << ", ";
858         }
859         sizes << ftFace->available_sizes[ i ].size;
860       }
861       DALI_LOG_ERROR( "FreeType Font: %s, does not contain Bitmaps of size: %d. Available sizes are: %s\n",
862                        path.c_str(), pointSize, sizes.str().c_str() );
863     }
864     else
865     {
866       error = FT_Set_Char_Size( ftFace,
867                               0,
868                               pointSize,
869                               mDpiHorizontal,
870                               mDpiVertical );
871
872       if( FT_Err_Ok == error )
873       {
874
875         FT_Size_Metrics& ftMetrics = ftFace->size->metrics;
876
877         FontMetrics metrics( static_cast< float >( ftMetrics.ascender  ) * FROM_266,
878                              static_cast< float >( ftMetrics.descender ) * FROM_266,
879                              static_cast< float >( ftMetrics.height    ) * FROM_266,
880                              static_cast< float >( ftFace->underline_position ) * FROM_266,
881                              static_cast< float >( ftFace->underline_thickness ) * FROM_266 );
882
883         mFontCache.push_back( CacheItem( ftFace, path, pointSize, faceIndex, metrics ) );
884         id = mFontCache.size();
885
886         if( cacheDescription )
887         {
888           FontDescription description;
889           description.path = path;
890           description.family = FontFamily( ftFace->family_name );
891           description.style = FontStyle( ftFace->style_name );
892
893           mFontDescriptionCache.push_back( description );
894         }
895       }
896       else
897       {
898         DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", error, pointSize );
899       }
900     }
901   }
902   else
903   {
904     DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
905   }
906
907   return id;
908 }
909
910 void FontClient::Plugin::ConvertBitmap( BufferImage& destBitmap,
911                                         FT_Bitmap srcBitmap )
912 {
913   if( srcBitmap.width*srcBitmap.rows > 0 )
914   {
915     switch( srcBitmap.pixel_mode )
916     {
917       case FT_PIXEL_MODE_GRAY:
918       {
919         if( srcBitmap.pitch == static_cast< int >( srcBitmap.width ) )
920         {
921           destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
922
923           PixelBuffer* destBuffer = destBitmap.GetBuffer();
924           if( destBuffer )
925           {
926             memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
927           }
928           else
929           {
930             DALI_LOG_ERROR( "GetBuffer returns null\n" );
931           }
932         }
933         break;
934       }
935
936 #ifdef FREETYPE_BITMAP_SUPPORT
937       case FT_PIXEL_MODE_BGRA:
938       {
939         if ( srcBitmap.pitch == static_cast< int >( srcBitmap.width << 2 ) )
940         {
941           destBitmap = BufferImage::New( srcBitmap.width, srcBitmap.rows, Pixel::BGRA8888 );
942
943           PixelBuffer* destBuffer = destBitmap.GetBuffer();
944           if( destBuffer )
945           {
946             memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows*4 );
947           }
948           else
949           {
950             DALI_LOG_ERROR( "GetBuffer returns null\n" );
951           }
952         }
953         break;
954       }
955 #endif
956       default:
957       {
958         DALI_LOG_ERROR( "FontClient Unable to create Bitmap of this PixelType\n" );
959         break;
960       }
961     }
962   }
963 }
964
965 bool FontClient::Plugin::FindFont( const FontPath& path,
966                                    PointSize26Dot6 pointSize,
967                                    FaceIndex faceIndex,
968                                    FontId& fontId ) const
969 {
970   fontId = 0u;
971   for( std::vector<CacheItem>::const_iterator it = mFontCache.begin(),
972          endIt = mFontCache.end();
973        it != endIt;
974        ++it, ++fontId )
975   {
976     const CacheItem& cacheItem = *it;
977
978     if( cacheItem.mPointSize == pointSize &&
979         cacheItem.mFaceIndex == faceIndex &&
980         cacheItem.mPath == path )
981     {
982       ++fontId;
983       return true;
984     }
985   }
986
987   return false;
988 }
989
990 bool FontClient::Plugin::FindValidatedFont( const FontFamily& fontFamily,
991                                             const FontStyle& fontStyle,
992                                             FontDescriptionId& validatedFontId )
993 {
994   validatedFontId = 0u;
995
996   for( std::vector<FontDescriptionCacheItem>::const_iterator it = mValidatedFontCache.begin(),
997          endIt = mValidatedFontCache.end();
998        it != endIt;
999        ++it )
1000   {
1001     const FontDescriptionCacheItem& item = *it;
1002
1003     if( ( fontFamily == item.fontFamily ) &&
1004         ( fontStyle == item.fontStyle ) )
1005     {
1006       validatedFontId = item.index;
1007
1008       return true;
1009     }
1010   }
1011
1012   return false;
1013 }
1014
1015 bool FontClient::Plugin::FindFont( FontDescriptionId validatedFontId,
1016                                    PointSize26Dot6 pointSize,
1017                                    FontId& fontId )
1018 {
1019   fontId = 0u;
1020
1021   for( std::vector<FontIdCacheItem>::const_iterator it = mFontIdCache.begin(),
1022          endIt = mFontIdCache.end();
1023        it != endIt;
1024        ++it )
1025   {
1026     const FontIdCacheItem& item = *it;
1027
1028     if( ( validatedFontId == item.validatedFontId ) &&
1029         ( pointSize == item.pointSize ) )
1030     {
1031       fontId = item.fontId;
1032       return true;
1033     }
1034   }
1035
1036   return false;
1037 }
1038
1039 bool FontClient::Plugin::IsScalable( const FontPath& path )
1040 {
1041   FT_Face ftFace;
1042   int error = FT_New_Face( mFreeTypeLibrary,
1043                            path.c_str(),
1044                            0,
1045                            &ftFace );
1046   if( FT_Err_Ok != error )
1047   {
1048     DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1049   }
1050   return ( ftFace->num_fixed_sizes == 0 );
1051 }
1052
1053 bool FontClient::Plugin::IsScalable( const FontFamily& fontFamily, const FontStyle& fontStyle )
1054 {
1055   // Create a font pattern.
1056   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamily,
1057                                                           fontStyle );
1058
1059   FcResult result = FcResultMatch;
1060
1061   // match the pattern
1062   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1063   bool isScalable = true;
1064
1065   if( match )
1066   {
1067     // Get the path to the font file name.
1068     FontPath path;
1069     GetFcString( match, FC_FILE, path );
1070     isScalable = IsScalable( path );
1071   }
1072   else
1073   {
1074     DALI_LOG_ERROR( "FreeType Cannot check font: %s %s\n", fontFamily.c_str(), fontStyle.c_str() );
1075   }
1076   FcPatternDestroy( fontFamilyPattern );
1077   FcPatternDestroy( match );
1078   return isScalable;
1079 }
1080
1081 void FontClient::Plugin::GetFixedSizes( const FontPath& path, Vector< PointSize26Dot6 >& sizes )
1082 {
1083   // Empty the caller container
1084   sizes.Clear();
1085
1086   FT_Face ftFace;
1087   int error = FT_New_Face( mFreeTypeLibrary,
1088                            path.c_str(),
1089                            0,
1090                            &ftFace );
1091   if( FT_Err_Ok != error )
1092   {
1093     DALI_LOG_ERROR( "FreeType Cannot check font: %s\n", path.c_str() );
1094   }
1095
1096   // Fetch the number of fixed sizes available
1097   if ( ftFace->num_fixed_sizes && ftFace->available_sizes )
1098   {
1099     for ( int i = 0; i < ftFace->num_fixed_sizes; ++i )
1100     {
1101       sizes.PushBack( ftFace->available_sizes[ i ].size );
1102     }
1103   }
1104 }
1105
1106 void FontClient::Plugin::GetFixedSizes( const FontFamily& fontFamily,
1107                                         const FontStyle& fontStyle,
1108                                         Vector< PointSize26Dot6 >& sizes )
1109 {
1110   // Create a font pattern.
1111   FcPattern* fontFamilyPattern = CreateFontFamilyPattern( fontFamily,
1112                                                           fontStyle );
1113
1114   FcResult result = FcResultMatch;
1115
1116   // match the pattern
1117   FcPattern* match = FcFontMatch( NULL /* use default configure */, fontFamilyPattern, &result );
1118
1119   if( match )
1120   {
1121     // Get the path to the font file name.
1122     FontPath path;
1123     GetFcString( match, FC_FILE, path );
1124     GetFixedSizes( path, sizes );
1125   }
1126   else
1127   {
1128     DALI_LOG_ERROR( "FreeType Cannot check font: %s %s\n", fontFamily.c_str(), fontStyle.c_str() );
1129   }
1130   FcPatternDestroy( match );
1131   FcPatternDestroy( fontFamilyPattern );
1132 }
1133
1134 } // namespace Internal
1135
1136 } // namespace TextAbstraction
1137
1138 } // namespace Dali