freetype: fix extra squares in Arabic subtitles (fixes #6152 #8117)
[vlc.git] / 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 > 0 )
133     {
134         FcPatternAddDouble( pat, FC_SIZE, (double)i_size );
135     }
136
137     /* */
138     FcDefaultSubstitute( pat );
139     if( !FcConfigSubstitute( config, pat, FcMatchPattern ) )
140     {
141         FcPatternDestroy( pat );
142         return NULL;
143     }
144
145     /* Find the best font for the pattern, destroy the pattern */
146     p_pat = FcFontMatch( config, pat, &result );
147     FcPatternDestroy( pat );
148     if( !p_pat || result == FcResultNoMatch ) return NULL;
149
150     /* Check the new pattern */
151     if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) )
152         || ( val_b != FcTrue ) )
153     {
154         FcPatternDestroy( p_pat );
155         return NULL;
156     }
157     if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) )
158     {
159         *i_idx = 0;
160     }
161
162     if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) )
163     {
164         FcPatternDestroy( p_pat );
165         return NULL;
166     }
167
168     /* if( strcasecmp((const char*)val_s, family ) != 0 )
169         msg_Warn( p_filter, "fontconfig: selected font family is not"
170                             "the requested one: '%s' != '%s'\n",
171                             (const char*)val_s, family );   */
172
173     if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) )
174         ret = strdup( (const char*)val_s );
175
176     FcPatternDestroy( p_pat );
177     return ret;
178 }
179 #endif
180
181 #if defined( _WIN32 ) && !VLC_WINSTORE_APP
182 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
183
184 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename )
185 {
186     HKEY hKey;
187     TCHAR vbuffer[MAX_PATH];
188     TCHAR dbuffer[256];
189
190     if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
191             != ERROR_SUCCESS )
192         return 1;
193
194     char *font_name_temp = FromT( font_name );
195     size_t fontname_len = strlen( font_name_temp );
196
197     for( int index = 0;; index++ )
198     {
199         DWORD vbuflen = MAX_PATH - 1;
200         DWORD dbuflen = 255;
201
202         LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
203                                       NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
204         if( i_result != ERROR_SUCCESS )
205         {
206             RegCloseKey( hKey );
207             return i_result;
208         }
209
210         char *psz_value = FromT( vbuffer );
211
212         char *s = strchr( psz_value,'(' );
213         if( s != NULL && s != psz_value ) s[-1] = '\0';
214
215         /* Manage concatenated font names */
216         if( strchr( psz_value, '&') ) {
217             if( strcasestr( psz_value, font_name_temp ) != NULL )
218             {
219                 free( psz_value );
220                 break;
221             }
222         }
223         else {
224             if( strncasecmp( psz_value, font_name_temp, fontname_len ) == 0 )
225             {
226                 free( psz_value );
227                 break;
228             }
229         }
230
231         free( psz_value );
232     }
233
234     *psz_filename = FromT( dbuffer );
235     free( font_name_temp );
236     RegCloseKey( hKey );
237     return 0;
238 }
239
240 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
241                                      DWORD type, LPARAM lParam)
242 {
243     VLC_UNUSED( metric );
244     if( (type & RASTER_FONTTYPE) ) return 1;
245     // if( lpelfe->elfScript ) FIXME
246
247     return GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, (char **)lParam );
248 }
249
250 static char* GetWindowsFontPath()
251 {
252     wchar_t wdir[MAX_PATH];
253     if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
254     {
255         GetWindowsDirectoryW( wdir, MAX_PATH );
256         wcscat( wdir, L"\\fonts" );
257     }
258     return FromWide( wdir );
259 }
260
261 char* Win32_Select( filter_t *p_filter, const char* family,
262                            bool b_bold, bool b_italic, int i_size, int *i_idx )
263 {
264     VLC_UNUSED( i_size );
265     VLC_UNUSED( i_idx );
266     VLC_UNUSED( p_filter );
267
268     if( !family || strlen( family ) < 1 )
269         goto fail;
270
271     /* */
272     LOGFONT lf;
273     lf.lfCharSet = DEFAULT_CHARSET;
274     if( b_italic )
275         lf.lfItalic = true;
276     if( b_bold )
277         lf.lfWeight = FW_BOLD;
278
279     LPTSTR psz_fbuffer = ToT( family );
280     _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
281     free( psz_fbuffer );
282
283     /* */
284     char *psz_filename = NULL;
285     HDC hDC = GetDC( NULL );
286     EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&psz_filename, 0);
287     ReleaseDC(NULL, hDC);
288
289     /* */
290     if( psz_filename != NULL )
291     {
292         /* FIXME: increase i_idx, when concatenated strings  */
293         i_idx = 0;
294
295         /* Prepend the Windows Font path, when only a filename was provided */
296         if( strchr( psz_filename, DIR_SEP_CHAR ) )
297             return psz_filename;
298         else
299         {
300             /* Get Windows Font folder */
301             char *psz_win_fonts_path = GetWindowsFontPath();
302             char *psz_tmp;
303             if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, psz_filename ) == -1 )
304             {
305                 free( psz_filename );
306                 free( psz_win_fonts_path );
307                 return NULL;
308             }
309             free( psz_filename );
310                 free( psz_win_fonts_path );
311
312             return psz_tmp;
313         }
314     }
315     else /* Let's take any font we can */
316 fail:
317     {
318         char *psz_win_fonts_path = GetWindowsFontPath();
319         char *psz_tmp;
320         if( asprintf( &psz_tmp, "%s\\%s", psz_win_fonts_path, SYSTEM_DEFAULT_FONT_FILE ) == -1 )
321             return NULL;
322         else
323             return psz_tmp;
324     }
325 }
326 #endif /* _WIN32 */
327
328 #ifdef __APPLE__
329 #if !TARGET_OS_IPHONE
330 char* MacLegacy_Select( filter_t *p_filter, const char* psz_fontname,
331                           bool b_bold, bool b_italic, int i_size, int *i_idx )
332 {
333     VLC_UNUSED( b_bold );
334     VLC_UNUSED( b_italic );
335     VLC_UNUSED( i_size );
336     FSRef ref;
337     unsigned char path[MAXPATHLEN];
338     char * psz_path;
339
340     CFStringRef  cf_fontName;
341     ATSFontRef   ats_font_id;
342
343     *i_idx = 0;
344
345     if( psz_fontname == NULL )
346         return NULL;
347
348     msg_Dbg( p_filter, "looking for %s", psz_fontname );
349     cf_fontName = CFStringCreateWithCString( kCFAllocatorDefault, psz_fontname, kCFStringEncodingUTF8 );
350
351     ats_font_id = ATSFontFindFromName( cf_fontName, kATSOptionFlagsIncludeDisabledMask );
352
353     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
354     {
355         msg_Dbg( p_filter, "ATS couldn't find %s by name, checking family", psz_fontname );
356         ats_font_id = ATSFontFamilyFindFromName( cf_fontName, kATSOptionFlagsDefault );
357
358         if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
359         {
360             msg_Dbg( p_filter, "ATS couldn't find either %s nor its family, checking PS name", psz_fontname );
361             ats_font_id = ATSFontFindFromPostScriptName( cf_fontName, kATSOptionFlagsDefault );
362
363             if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
364             {
365                 msg_Err( p_filter, "ATS couldn't find %s (no font name, family or PS name)", psz_fontname );
366                 CFRelease( cf_fontName );
367                 return NULL;
368             }
369         }
370     }
371     CFRelease( cf_fontName );
372
373     if ( noErr != ATSFontGetFileReference( ats_font_id, &ref ) )
374     {
375         msg_Err( p_filter, "ATS couldn't get file ref for %s", psz_fontname );
376         return NULL;
377     }
378
379     /* i_idx calculation by searching preceding fontIDs */
380     /* with same FSRef                                       */
381     {
382         ATSFontRef  id2 = ats_font_id - 1;
383         FSRef       ref2;
384
385         while ( id2 > 0 )
386         {
387             if ( noErr != ATSFontGetFileReference( id2, &ref2 ) )
388                 break;
389             if ( noErr != FSCompareFSRefs( &ref, &ref2 ) )
390                 break;
391
392             id2 --;
393         }
394         *i_idx = ats_font_id - ( id2 + 1 );
395     }
396
397     if ( noErr != FSRefMakePath( &ref, path, sizeof(path) ) )
398     {
399         msg_Err( p_filter, "failure when getting path from FSRef" );
400         return NULL;
401     }
402     msg_Dbg( p_filter, "found %s", path );
403
404     psz_path = strdup( (char *)path );
405
406     return psz_path;
407 }
408 #endif
409 #endif
410
411 char* Dummy_Select( filter_t *p_filter, const char* psz_font,
412                     bool b_bold, bool b_italic, int i_size, int *i_idx )
413 {
414     VLC_UNUSED(p_filter);
415     VLC_UNUSED(b_bold);
416     VLC_UNUSED(b_italic);
417     VLC_UNUSED(i_size);
418     VLC_UNUSED(i_idx);
419
420     char *psz_fontname;
421 # if defined( _WIN32 ) && !VLC_WINSTORE_APP
422     /* Get Windows Font folder */
423     char *psz_win_fonts_path = GetWindowsFontPath();
424     if( asprintf( &psz_fontname, "%s\\%s", psz_win_fonts_path, psz_font ) == -1 )
425     {
426         psz_fontname = NULL;
427         return NULL;
428     }
429     free(psz_win_fonts_path);
430 # else
431     psz_fontname = strdup( psz_font );
432 # endif
433
434     return psz_fontname;
435 }