Moved TextAbstraction to use absolute paths
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / font-client-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 "font-client-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <vector>
23 #include <ft2build.h>
24 #include FT_FREETYPE_H
25 #include FT_GLYPH_H
26 #include <fontconfig/fontconfig.h>
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL INCLUDES
30 #include <singleton-service-impl.h>
31
32 namespace Dali
33 {
34
35 namespace TextAbstraction
36 {
37
38 namespace Internal
39 {
40
41 struct FontClient::Plugin
42 {
43   struct CacheItem
44   {
45     CacheItem( FontId id, FT_Face ftFace, const std::string& path, PointSize26Dot6 pointSize, FaceIndex face )
46     : mFreeTypeFace( ftFace ),
47       mPath( path ),
48       mPointSize( pointSize ),
49       mFaceIndex( face )
50     {
51     }
52
53     FT_Face mFreeTypeFace;
54     std::string mPath;
55     PointSize26Dot6 mPointSize;
56     FaceIndex mFaceIndex;
57   };
58
59   Plugin( unsigned int horizontalDpi, unsigned int verticalDpi )
60   : mFreeTypeLibrary( NULL ),
61     mDpiHorizontal( horizontalDpi ),
62     mDpiVertical( verticalDpi )
63   {
64   }
65
66   ~Plugin()
67   {
68     FT_Done_FreeType( mFreeTypeLibrary );
69   }
70
71   void Initialize()
72   {
73     int error = FT_Init_FreeType( &mFreeTypeLibrary );
74     if( FT_Err_Ok != error )
75     {
76       DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
77     }
78   }
79
80   void SetDpi( unsigned int horizontalDpi, unsigned int verticalDpi )
81   {
82     mDpiHorizontal = horizontalDpi;
83     mDpiVertical = verticalDpi;
84   }
85
86   void GetSystemFonts( FontList& systemFonts )
87   {
88     if( mSystemFonts.empty() )
89     {
90       InitSystemFonts();
91     }
92
93     systemFonts = mSystemFonts;
94   }
95
96   void InitSystemFonts()
97   {
98     FcFontSet* fontSet = GetFcFontSet();
99
100     if( fontSet )
101     {
102       mSystemFonts.reserve( fontSet->nfont );
103
104       for( int i = 0u; i < fontSet->nfont; ++i )
105       {
106         FcPattern* fontPattern = fontSet->fonts[i];
107
108         std::string path;
109
110         // Skip fonts with no path
111         if( GetFcString( fontPattern, FC_FILE, path ) )
112         {
113           mSystemFonts.push_back( FontDescription() );
114           FontDescription& fontDescription = mSystemFonts.back();
115
116           fontDescription.path = path;
117
118           GetFcString( fontPattern, FC_FAMILY, fontDescription.family );
119           GetFcString( fontPattern, FC_STYLE, fontDescription.style );
120         }
121       }
122
123       FcFontSetDestroy( fontSet );
124     }
125   }
126
127   _FcFontSet* GetFcFontSet() const
128   {
129     // create a new pattern.
130     // a pattern holds a set of names, each name refers to a property of the font
131     FcPattern* pattern = FcPatternCreate();
132
133     // create an object set used to define which properties are to be returned in the patterns from FcFontList.
134     FcObjectSet* objectSet = FcObjectSetCreate();
135
136     // build an object set from a list of property names
137     FcObjectSetAdd( objectSet, FC_FILE );
138     FcObjectSetAdd( objectSet, FC_FAMILY );
139     FcObjectSetAdd( objectSet, FC_STYLE );
140
141     // get a list of fonts
142     // creates patterns from those fonts containing only the objects in objectSet and returns the set of unique such patterns
143     FcFontSet* fontset = FcFontList( NULL /* the default configuration is checked to be up to date, and used */, pattern, objectSet );
144
145     // clear up the object set
146     if( objectSet )
147     {
148       FcObjectSetDestroy( objectSet );
149     }
150     // clear up the pattern
151     if( pattern )
152     {
153       FcPatternDestroy( pattern );
154     }
155
156     return fontset;
157   }
158
159   bool GetFcString( const FcPattern* pattern, const char* n, std::string& string )
160   {
161     FcChar8* file = NULL;
162     const FcResult retVal = FcPatternGetString( pattern, n, 0u, &file );
163
164     if( FcResultMatch == retVal )
165     {
166       // Have to use reinterpret_cast because FcChar8 is unsigned char*, not a const char*.
167       string.assign( reinterpret_cast<const char*>( file ) );
168
169       return true;
170     }
171
172     return false;
173   }
174
175   bool FindSystemFont( Character charcode, FontDescription& systemFont )
176   {
177     // TODO - Use FcCharSetHasChar()
178
179     return false;
180   }
181
182   FontId GetFontId( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
183   {
184     FontId id( 0 );
185
186     if( NULL != mFreeTypeLibrary )
187     {
188       FontId foundId(0);
189       if( FindFont( path, pointSize, faceIndex, foundId ) )
190       {
191         id = foundId;
192       }
193       else
194       {
195         id = CreateFont( path, pointSize, faceIndex );
196       }
197     }
198
199     return id;
200   }
201
202   GlyphIndex GetGlyphIndex( FontId fontId, Character charcode )
203   {
204     GlyphIndex index( 0 );
205
206     if( fontId > 0 &&
207         fontId-1 < mFontCache.size() )
208     {
209       FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
210
211       index = FT_Get_Char_Index( ftFace, charcode );
212     }
213
214     return index;
215   }
216
217   FontId CreateFont( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
218   {
219     FontId id( 0 );
220
221     // Create & cache new font face
222     FT_Face ftFace;
223     int error = FT_New_Face( mFreeTypeLibrary,
224                              path.c_str(),
225                              0,
226                              &ftFace );
227
228     if( FT_Err_Ok == error )
229     {
230       error = FT_Set_Char_Size( ftFace,
231                                 0,
232                                 pointSize,
233                                 mDpiHorizontal,
234                                 mDpiVertical );
235
236       if( FT_Err_Ok == error )
237       {
238         id = mFontCache.size() + 1;
239         mFontCache.push_back( CacheItem( id, ftFace, path, pointSize, faceIndex ) );
240       }
241       else
242       {
243         DALI_LOG_ERROR( "FreeType Set_Char_Size error: %d for pointSize %d\n", pointSize );
244       }
245     }
246     else
247     {
248       DALI_LOG_ERROR( "FreeType New_Face error: %d for %s\n", error, path.c_str() );
249     }
250
251     return id;
252   }
253
254   FontId FindDefaultFont( Character charcode )
255   {
256     // TODO - Return cached results based on script
257     return FontId(0);
258   }
259
260   bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal )
261   {
262     bool success( true );
263
264     for( unsigned int i=0; i<size; ++i )
265     {
266       FontId fontId = array[i].fontId;
267
268       if( fontId > 0 &&
269           fontId-1 < mFontCache.size() )
270       {
271         FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
272
273         int error = FT_Load_Glyph( ftFace, array[i].index, FT_LOAD_DEFAULT );
274
275         if( FT_Err_Ok == error )
276         {
277           array[i].width  = ftFace->glyph->metrics.width;
278           array[i].height = ftFace->glyph->metrics.height;
279           if( horizontal )
280           {
281             array[i].xBearing = ftFace->glyph->metrics.horiBearingX;
282             array[i].yBearing = ftFace->glyph->metrics.horiBearingY;
283             array[i].advance  = ftFace->glyph->metrics.horiAdvance;
284           }
285           else
286           {
287             array[i].xBearing = ftFace->glyph->metrics.vertBearingX;
288             array[i].yBearing = ftFace->glyph->metrics.vertBearingY;
289             array[i].advance  = ftFace->glyph->metrics.vertAdvance;
290           }
291         }
292         else
293         {
294           success = false;
295         }
296       }
297       else
298       {
299         success = false;
300       }
301     }
302
303     return success;
304   }
305
306   BitmapImage CreateBitmap( FontId fontId, GlyphIndex glyphIndex )
307   {
308     BitmapImage bitmap;
309
310     if( fontId > 0 &&
311         fontId-1 < mFontCache.size() )
312     {
313       FT_Face ftFace = mFontCache[fontId-1].mFreeTypeFace;
314
315       FT_Error error = FT_Load_Glyph( ftFace, glyphIndex, FT_LOAD_DEFAULT );
316       if( FT_Err_Ok == error )
317       {
318         FT_Glyph glyph;
319         error = FT_Get_Glyph( ftFace->glyph, &glyph );
320
321         // Convert to bitmap if necessary
322         if ( FT_Err_Ok == error )
323         {
324           if( glyph->format != FT_GLYPH_FORMAT_BITMAP )
325           {
326             error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
327           }
328           else
329           {
330             DALI_LOG_ERROR( "FT_Glyph_To_Bitmap Failed with error: %d\n", error );
331           }
332         }
333         else
334         {
335           DALI_LOG_ERROR( "FT_Get_Glyph Failed with error: %d\n", error );
336         }
337
338         if( FT_Err_Ok == error )
339         {
340           // Access the underlying bitmap data
341           FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph;
342           ConvertBitmap( bitmap, bitmapGlyph->bitmap );
343         }
344
345         // Created FT_Glyph object must be released with FT_Done_Glyph
346         FT_Done_Glyph( glyph );
347       }
348       else
349       {
350         DALI_LOG_ERROR( "FT_Load_Glyph Failed with error: %d\n", error );
351       }
352     }
353
354     return bitmap;
355   }
356
357   void ConvertBitmap( BitmapImage& destBitmap, FT_Bitmap srcBitmap )
358   {
359     if( srcBitmap.width*srcBitmap.rows > 0 )
360     {
361       // TODO - Support all pixel modes
362       if( FT_PIXEL_MODE_GRAY == srcBitmap.pixel_mode )
363       {
364         if( srcBitmap.pitch == srcBitmap.width )
365         {
366           destBitmap = BitmapImage::New( srcBitmap.width, srcBitmap.rows, Pixel::L8 );
367
368           PixelBuffer* destBuffer = destBitmap.GetBuffer();
369           memcpy( destBuffer, srcBitmap.buffer, srcBitmap.width*srcBitmap.rows );
370         }
371       }
372     }
373   }
374
375 private:
376
377   bool FindFont( const std::string& path, PointSize26Dot6 pointSize, FaceIndex faceIndex, FontId& found ) const
378   {
379     for( unsigned int i=0; i<mFontCache.size(); ++i )
380     {
381       if( mFontCache[i].mPointSize == pointSize &&
382           mFontCache[i].mPath == path &&
383           mFontCache[i].mFaceIndex == faceIndex )
384       {
385         found = i + 1;
386         return true;
387       }
388     }
389
390     return false;
391   }
392
393   FT_Library mFreeTypeLibrary;
394
395   FontList mSystemFonts;
396
397   std::vector<CacheItem> mFontCache;
398
399   unsigned int mDpiHorizontal;
400   unsigned int mDpiVertical;
401 };
402
403 FontClient::FontClient()
404 : mPlugin( NULL ),
405   mDpiHorizontal( 0 ),
406   mDpiVertical( 0 )
407 {
408 }
409
410 FontClient::~FontClient()
411 {
412   delete mPlugin;
413 }
414
415 Dali::TextAbstraction::FontClient FontClient::Get()
416 {
417   Dali::TextAbstraction::FontClient fontClientHandle;
418
419   Dali::SingletonService service( SingletonService::Get() );
420   if ( service )
421   {
422     // Check whether the singleton is already created
423     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::TextAbstraction::FontClient ) );
424     if(handle)
425     {
426       // If so, downcast the handle
427       FontClient* impl = dynamic_cast< Dali::TextAbstraction::Internal::FontClient* >( handle.GetObjectPtr() );
428       fontClientHandle = Dali::TextAbstraction::FontClient( impl );
429     }
430     else // create and register the object
431     {
432       fontClientHandle = Dali::TextAbstraction::FontClient( new FontClient );
433       service.Register( typeid( fontClientHandle ), fontClientHandle );
434     }
435   }
436
437   return fontClientHandle;
438 }
439
440 void FontClient::SetDpi( unsigned int horizontalDpi, unsigned int verticalDpi  )
441 {
442   mDpiHorizontal = horizontalDpi;
443   mDpiVertical = verticalDpi;
444
445   // Allow DPI to be set without loading plugin
446   if( mPlugin )
447   {
448     mPlugin->SetDpi( horizontalDpi, verticalDpi  );
449   }
450 }
451
452 void FontClient::GetSystemFonts( FontList& systemFonts )
453 {
454   CreatePlugin();
455
456   mPlugin->GetSystemFonts( systemFonts );
457 }
458
459 bool FontClient::FindSystemFont( Character charcode, FontDescription& systemFont )
460 {
461   CreatePlugin();
462
463   return mPlugin->FindSystemFont( charcode, systemFont );
464 }
465
466 FontId FontClient::GetFontId( const FontPath& path, PointSize26Dot6 pointSize, FaceIndex faceIndex )
467 {
468   CreatePlugin();
469
470   return mPlugin->GetFontId( path, pointSize, faceIndex );
471 }
472
473 FontId FontClient::FindDefaultFont( Character charcode )
474 {
475   CreatePlugin();
476
477   return mPlugin->FindDefaultFont( charcode );
478 }
479
480 GlyphIndex FontClient::GetGlyphIndex( FontId fontId, Character charcode )
481 {
482   CreatePlugin();
483
484   return mPlugin->GetGlyphIndex( fontId, charcode );
485 }
486
487 bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal )
488 {
489   CreatePlugin();
490
491   return mPlugin->GetGlyphMetrics( array, size, horizontal );
492 }
493
494 BitmapImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex )
495 {
496   CreatePlugin();
497
498   return mPlugin->CreateBitmap( fontId, glyphIndex );
499 }
500
501 void FontClient::CreatePlugin()
502 {
503   if( !mPlugin )
504   {
505     mPlugin = new Plugin( mDpiHorizontal, mDpiVertical );
506     mPlugin->Initialize();
507   }
508 }
509
510 } // namespace Internal
511
512 } // namespace FontClient
513
514 } // namespace Dali