]> git.sesse.net Git - vlc/blob - modules/text_renderer/platform_fonts.c
Move font defaults to platform_fonts.h
[vlc] / modules / text_renderer / platform_fonts.c
1 /*****************************************************************************
2  * freetype.c : Put text on the video, using freetype2
3  *****************************************************************************
4  * Copyright (C) 2002 - 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Bernie Purcell <bitmap@videolan.org>
10  *          Jean-Baptiste Kempf <jb@videolan.org>
11  *          Felix Paul Kühne <fkuehne@videolan.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation, Inc.,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc_common.h>
37 #include <vlc_filter.h>                                      /* filter_sys_t */
38 #include <vlc_text_style.h>                                   /* text_style_t*/
39
40 /* apple stuff */
41 #ifdef __APPLE__
42 #include <TargetConditionals.h>
43 #if !TARGET_OS_IPHONE
44 #include <Carbon/Carbon.h>
45 #endif
46 #include <sys/param.h>                         /* for MAXPATHLEN */
47 #undef HAVE_FONTCONFIG
48 #endif
49
50 /* Win32 GDI */
51 #ifdef _WIN32
52 # include <windows.h>
53 # include <shlobj.h>
54 # include <vlc_charset.h>                                     /* FromT */
55 #endif
56
57 /* FontConfig */
58 #ifdef HAVE_FONTCONFIG
59 # include <fontconfig/fontconfig.h>
60 #endif
61
62 #include "platform_fonts.h"
63
64 #ifdef HAVE_FONTCONFIG
65 void FontConfig_BuildCache( filter_t *p_filter )
66 {
67     /* */
68     msg_Dbg( p_filter, "Building font databases.");
69     mtime_t t1, t2;
70     t1 = mdate();
71
72 #ifdef __OS2__
73     FcInit();
74 #endif
75
76 #if defined( _WIN32 ) || defined( __APPLE__ )
77     dialog_progress_bar_t *p_dialog = NULL;
78     FcConfig *fcConfig = FcInitLoadConfig();
79
80     p_dialog = dialog_ProgressCreate( p_filter,
81             _("Building font cache"),
82             _("Please wait while your font cache is rebuilt.\n"
83                 "This should take less than a few minutes."), NULL );
84
85 /*    if( p_dialog )
86         dialog_ProgressSet( p_dialog, NULL, 0.5 ); */
87
88     FcConfigBuildFonts( fcConfig );
89 #if defined( __APPLE__ )
90     // By default, scan only the directory /System/Library/Fonts.
91     // So build the set of available fonts under another directories,
92     // and add the set to the current configuration.
93     FcConfigAppFontAddDir( NULL, "~/Library/Fonts" );
94     FcConfigAppFontAddDir( NULL, "/Library/Fonts" );
95     FcConfigAppFontAddDir( NULL, "/Network/Library/Fonts" );
96     //FcConfigAppFontAddDir( NULL, "/System/Library/Fonts" );
97 #endif
98     if( p_dialog )
99     {
100 //        dialog_ProgressSet( p_dialog, NULL, 1.0 );
101         dialog_ProgressDestroy( p_dialog );
102         p_dialog = NULL;
103     }
104 #endif
105     t2 = mdate();
106     msg_Dbg( p_filter, "Took %ld microseconds", (long)((t2 - t1)) );
107 }
108
109 /***
110  * \brief Selects a font matching family, bold, italic provided
111  ***/
112 char* FontConfig_Select( filter_t *p_filter, const char* family,
113                           bool b_bold, bool b_italic, int i_size, int *i_idx )
114 {
115     FcResult result = FcResultMatch;
116     FcPattern *pat, *p_pat;
117     FcChar8* val_s;
118     FcBool val_b;
119     char *ret = NULL;
120     FcConfig* config = NULL;
121     VLC_UNUSED(p_filter);
122
123     /* Create a pattern and fills it */
124     pat = FcPatternCreate();
125     if (!pat) return NULL;
126
127     /* */
128     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
129     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
130     FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
131     FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
132     if( i_size != -1 )
133     {
134         char *psz_fontsize;
135         if( asprintf( &psz_fontsize, "%d", i_size ) != -1 )
136         {
137             FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize );
138             free( psz_fontsize );
139         }
140     }
141
142     /* */
143     FcDefaultSubstitute( pat );
144     if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
145     {
146         FcPatternDestroy( pat );
147         return NULL;
148     }
149
150     /* Find the best font for the pattern, destroy the pattern */
151     p_pat = FcFontMatch( config, pat, &result );
152     FcPatternDestroy( pat );
153     if( !p_pat || result == FcResultNoMatch ) return NULL;
154
155     /* Check the new pattern */
156     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
157         || ( val_b != FcTrue ) )
158     {
159         FcPatternDestroy( p_pat );
160         return NULL;
161     }
162     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
163     {
164         *i_idx = 0;
165     }
166
167     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
168     {
169         FcPatternDestroy( p_pat );
170         return NULL;
171     }
172
173     /* if( strcasecmp((const char*)val_s, family ) != 0 )
174         msg_Warn( p_filter, "fontconfig: selected font family is not"
175                             "the requested one: '%s' != '%s'\n",
176                             (const char*)val_s, family );   */
177
178     if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
179         ret = strdup( (const char*)val_s );
180
181     FcPatternDestroy( p_pat );
182     return ret;
183 }
184 #endif
185
186 #ifdef _WIN32
187 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
188
189 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
190 {
191     HKEY hKey;
192     TCHAR vbuffer[MAX_PATH];
193     TCHAR dbuffer[256];
194
195     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
196             != ERROR_SUCCESS )
197         return 1;
198
199     char *font_name_temp = FromT( font_name );
200     size_t fontname_len = strlen( font_name_temp );
201
202     for( int index = 0;; index++ )
203     {
204         DWORD vbuflen = MAX_PATH - 1;
205         DWORD dbuflen = 255;
206
207         LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
208                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
209         if( i_result != ERROR_SUCCESS )
210         {
211             RegCloseKey( hKey );
212             return i_result;
213         }
214
215         char *psz_value = FromT( vbuffer );
216
217         char *s = strchr( psz_value,'(' );
218         if( s != NULL && s != psz_value ) s[-1] = '\0';
219
220         /* Manage concatenated font names */
221         if( strchr( psz_value, '&') ) {
222             if( strcasestr( psz_value, font_name_temp ) != NULL )
223             {
224                 free( psz_value );
225                 break;
226             }
227         }
228         else {
229             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
230             {
231                 free( psz_value );
232                 break;
233             }
234         }
235
236         free( psz_value );
237     }
238
239     *psz_filename = FromT( dbuffer );
240     free( font_name_temp );
241     RegCloseKey( hKey );
242     return 0;
243 }
244
245 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
246                                      DWORD type, LPARAM lParam)
247 {
248     VLC_UNUSED( metric );
249     if( (type & RASTER_FONTTYPE) ) return 1;
250     // if( lpelfe->elfScript ) FIXME
251
252     return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
253 }
254
255 char* GetWindowsFontPath()
256 {
257     wchar_t wdir[MAX_PATH];
258     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
259     {
260         GetWindowsDirectoryW( wdir, MAX_PATH );
261         wcscat( wdir, L"\\fonts" );
262     }
263     return FromWide( wdir );
264 }
265
266 char* Win32_Select( filter_t *p_filter, const char* family,
267                            bool b_bold, bool b_italic, int i_size, int *i_idx )
268 {
269     VLC_UNUSED( i_size );
270     VLC_UNUSED( i_idx );
271     VLC_UNUSED( p_filter );
272
273     if( !family || strlen( family ) < 1 )
274         goto fail;
275
276     /* */
277     LOGFONT lf;
278     lf.lfCharSet = DEFAULT_CHARSET;
279     if( b_italic )
280         lf.lfItalic = true;
281     if( b_bold )
282         lf.lfWeight = FW_BOLD;
283
284     LPTSTR psz_fbuffer = ToT( family );
285     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
286     free( psz_fbuffer );
287
288     /* */
289     char *psz_filename = NULL;
290     HDC hDC = GetDC( NULL );
291     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
292     ReleaseDC(NULL, hDC);
293
294     /* */
295     if( psz_filename != NULL )
296     {
297         /* FIXME: increase i_idx, when concatenated strings  */
298         i_idx = 0;
299
300         /* Prepend the Windows Font path, when only a filename was provided */
301         if( strchr( psz_filename, DIR_SEP_CHAR ) )
302             return psz_filename;
303         else
304         {
305             /* Get Windows Font folder */
306             char *psz_win_fonts_path = GetWindowsFontPath();
307             char *psz_tmp;
308             if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, psz_filename ) == -1 )
309             {
310                 free( psz_filename );
311                 free( psz_win_fonts_path );
312                 return NULL;
313             }
314             free( psz_filename );
315                 free( psz_win_fonts_path );
316
317             return psz_tmp;
318         }
319     }
320     else /* Let's take any font we can */
321 fail:
322     {
323         char *psz_win_fonts_path = GetWindowsFontPath();
324         char *psz_tmp;
325         if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, SYSTEM_DEFAULT_FONT_FILE ) == -1 )
326             return NULL;
327         else
328             return psz_tmp;
329     }
330 }
331 #endif /* _WIN32 */
332
333 #ifdef __APPLE__
334 #if !TARGET_OS_IPHONE
335 char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
336                           bool b_bold, bool b_italic, int i_size, int *i_idx )
337 {
338     VLC_UNUSED( b_bold );
339     VLC_UNUSED( b_italic );
340     VLC_UNUSED( i_size );
341     FSRef ref;
342     unsigned char path[MAXPATHLEN];
343     char * psz_path;
344
345     CFStringRef  cf_fontName;
346     ATSFontRef   ats_font_id;
347
348     *i_idx = 0;
349
350     if( psz_fontname == NULL )
351         return NULL;
352
353     msg_Dbg( p_filter, "looking for %s", psz_fontname );
354     cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
355
356     ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
357
358     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
359     {
360         msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
361         ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
362
363         if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
364         {
365             msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
366             ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
367
368             if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
369             {
370                 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
371                 CFRelease( cf_fontName );
372                 return NULL;
373             }
374         }
375     }
376     CFRelease( cf_fontName );
377
378     if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
379     {
380         msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
381         return NULL;
382     }
383
384     /* i_idx calculation by searching preceding fontIDs */
385     /* with same FSRef                                       */
386     {
387         ATSFontRef  id2 = ats_font_id - 1;
388         FSRef       ref2;
389
390         while ( id2 > 0 )
391         {
392             if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
393                 break;
394             if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
395                 break;
396
397             id2 --;
398         }
399         *i_idx = ats_font_id - ( id2 + 1 );
400     }
401
402     if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
403     {
404         msg_Err( p_filter, "failure when getting path from FSRef" );
405         return NULL;
406     }
407     msg_Dbg( p_filter, "found %s", path );
408
409     psz_path = strdup( (char *)path );
410
411     return psz_path;
412 }
413 #endif
414 #endif
415