1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002 - 2015 VLC authors and VideoLAN
5 * $Id: 73d4939f07eb5022f1eb2f313f98a88a8527de34 $
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 * Salah-Eddin Shaban <salshaaban@gmail.com>
13 *
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation, Inc.,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
28
29 /*****************************************************************************
30 * Preamble
31 *****************************************************************************/
32
33 /** \ingroup freetype_fonts
34 * @{
35 * \file
36 * Win32 font management
37 */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include <vlc_common.h>
44 #include <vlc_filter.h>
45
46 #include <ft2build.h>
47 #include FT_SFNT_NAMES_H
48
49 /* Win32 GDI */
50 #ifdef _WIN32
51 # include <windows.h>
52 # include <shlobj.h>
53 # include <usp10.h>
54 # include <vlc_charset.h> /* FromT */
55 # undef HAVE_FONTCONFIG
56 #endif
57
58 #include "../platform_fonts.h"
59
60 #if !VLC_WINSTORE_APP
61 #define FONT_DIR_NT _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")
62
AppendFamily(vlc_family_t ** pp_list,vlc_family_t * p_family)63 static inline void AppendFamily( vlc_family_t **pp_list, vlc_family_t *p_family )
64 {
65 while( *pp_list )
66 pp_list = &( *pp_list )->p_next;
67
68 *pp_list = p_family;
69 }
70
71
Trim(char * psz_text)72 static char *Trim( char *psz_text )
73 {
74 int i_first_char = -1;
75 int i_last_char = -1;
76 int i_len = strlen( psz_text );
77
78 for( int i = 0; i < i_len; ++i )
79 {
80 if( psz_text[i] != ' ')
81 {
82 if( i_first_char == -1 )
83 i_first_char = i;
84 i_last_char = i;
85 }
86 }
87
88 psz_text[ i_last_char + 1 ] = 0;
89 if( i_first_char != -1 ) psz_text = psz_text + i_first_char;
90
91 return psz_text;
92 }
93
ConcatenatedIndex(char * psz_haystack,const char * psz_needle)94 static int ConcatenatedIndex( char *psz_haystack, const char *psz_needle )
95 {
96 char *psz_family = psz_haystack;
97 char *psz_terminator = psz_haystack + strlen( psz_haystack );
98 int i_index = 0;
99
100 while( psz_family < psz_terminator )
101 {
102 char *psz_amp = strchr( psz_family, '&' );
103
104 if( !psz_amp ) psz_amp = psz_terminator;
105
106 *psz_amp = 0;
107
108 psz_family = Trim( psz_family );
109 if( !strcasecmp( psz_family, psz_needle ) )
110 return i_index;
111
112 psz_family = psz_amp + 1;
113 ++i_index;
114 }
115
116 return -1;
117 }
118
GetFileFontByName(LPCTSTR font_name,char ** psz_filename,int * i_index)119 static int GetFileFontByName( LPCTSTR font_name, char **psz_filename, int *i_index )
120 {
121 HKEY hKey;
122 TCHAR vbuffer[MAX_PATH];
123 TCHAR dbuffer[256];
124
125 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, FONT_DIR_NT, 0, KEY_READ, &hKey)
126 != ERROR_SUCCESS )
127 return 1;
128
129 char *font_name_temp = FromT( font_name );
130
131 for( int index = 0;; index++ )
132 {
133 DWORD vbuflen = MAX_PATH - 1;
134 DWORD dbuflen = 255;
135
136 LONG i_result = RegEnumValue( hKey, index, vbuffer, &vbuflen,
137 NULL, NULL, (LPBYTE)dbuffer, &dbuflen);
138 if( i_result != ERROR_SUCCESS )
139 {
140 free( font_name_temp );
141 RegCloseKey( hKey );
142 return i_result;
143 }
144
145 char *psz_value = FromT( vbuffer );
146
147 char *s = strchr( psz_value,'(' );
148 if( s != NULL && s != psz_value ) s[-1] = '\0';
149
150 int i_concat_idx = 0;
151 if( ( i_concat_idx = ConcatenatedIndex( psz_value, font_name_temp ) ) != -1 )
152 {
153 *i_index = i_concat_idx;
154 *psz_filename = FromT( dbuffer );
155 free( psz_value );
156 break;
157 }
158
159 free( psz_value );
160 }
161
162 free( font_name_temp );
163 RegCloseKey( hKey );
164 return 0;
165 }
166
GetWindowsFontPath()167 static char* GetWindowsFontPath()
168 {
169 wchar_t wdir[MAX_PATH];
170 if( S_OK != SHGetFolderPathW( NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, wdir ) )
171 {
172 GetWindowsDirectoryW( wdir, MAX_PATH );
173 wcscat( wdir, L"\\fonts" );
174 }
175 return FromWide( wdir );
176 }
177
178 /**
179 * Get an SFNT name entry from an SFNT name table.
180 *
181 * \param p_name_data the start position of the name entry within the table [IN]
182 * \param p_storage_start the start position of the string storage area in the table [IN]
183 * \param p_table_end the position after the last byte of the table [IN]
184 * \param p_sfnt_name pointer to a \ref FT_SfntName to fill [OUT]
185 *
186 * \return \ref VLC_SUCCESS or \ref VLC_EGENERIC
187 */
GetSfntNameEntry(FT_Byte * p_name_data,FT_Byte * p_storage_start,FT_Byte * p_table_end,FT_SfntName * p_sfnt_name)188 static int GetSfntNameEntry( FT_Byte *p_name_data, FT_Byte *p_storage_start,
189 FT_Byte *p_table_end, FT_SfntName *p_sfnt_name )
190 {
191 uint16_t i_string_len = U16_AT( p_name_data + 8 );
192 uint16_t i_string_offset = U16_AT( p_name_data + 10 );
193 if( i_string_len == 0
194 || p_storage_start + i_string_offset + i_string_len > p_table_end )
195 return VLC_EGENERIC;
196
197 p_sfnt_name->platform_id = U16_AT( p_name_data + 0 );
198 p_sfnt_name->encoding_id = U16_AT( p_name_data + 2 );
199 p_sfnt_name->language_id = U16_AT( p_name_data + 4 );
200 p_sfnt_name->name_id = U16_AT( p_name_data + 6 );
201 p_sfnt_name->string_len = i_string_len;
202 p_sfnt_name->string = p_storage_start + i_string_offset;
203
204 return VLC_SUCCESS;
205 }
206
207 /**
208 * Get the SFNT name string matching the specified platform, encoding, name, and language IDs.
209 *
210 *\param p_table the SFNT table [IN]
211 *\param i_size table size in bytes [IN]
212 *\param i_platform_id platform ID as specified in the TrueType spec [IN]
213 *\param i_encoding_id encoding ID as specified in the TrueType spec [IN]
214 *\param i_name_id name ID as specified in the TrueType spec [IN]
215 *\param i_language_id language ID as specified in the TrueType spec [IN]
216 *\param pp_name the requested name.
217 * This is not null terminated. And can have different encodings
218 * based on the specified platform/encoding IDs [OUT]
219 *\param i_name_length the length in bytes of the returned name [OUT]
220 *
221 *\return \ref VLC_SUCCESS or \ref VLC_EGENERIC
222 */
GetSfntNameString(FT_Byte * p_table,FT_UInt i_size,FT_UShort i_platform_id,FT_UShort i_encoding_id,FT_UShort i_name_id,FT_UShort i_language_id,FT_Byte ** pp_name,FT_UInt * i_name_length)223 static int GetSfntNameString( FT_Byte *p_table, FT_UInt i_size, FT_UShort i_platform_id,
224 FT_UShort i_encoding_id, FT_UShort i_name_id, FT_UShort i_language_id,
225 FT_Byte **pp_name, FT_UInt *i_name_length )
226 {
227 uint16_t i_name_count = U16_AT( p_table + 2 );
228 uint16_t i_storage_offset = U16_AT( p_table + 4 );
229 FT_Byte *p_storage = p_table + i_storage_offset;
230 FT_Byte *p_names = p_table + 6;
231
232 const int i_entry_size = 12;
233
234 for(int i = 0; i < i_name_count; ++i)
235 {
236 FT_SfntName sfnt_name;
237
238 if( GetSfntNameEntry( p_names + i * i_entry_size, p_storage, p_table + i_size, &sfnt_name ) )
239 return VLC_EGENERIC;
240
241 if( sfnt_name.platform_id == i_platform_id && sfnt_name.encoding_id == i_encoding_id
242 && sfnt_name.name_id == i_name_id && sfnt_name.language_id == i_language_id )
243 {
244 *i_name_length = sfnt_name.string_len;
245 *pp_name = sfnt_name.string;
246
247 return VLC_SUCCESS;
248 }
249 }
250
251 return VLC_EGENERIC;
252 }
253
254 /**
255 * Get the font's full English name.
256 *
257 * If the font has a family name or style name that matches the
258 * system's locale, EnumFontCallback() will be called with a \ref ENUMLOGFONTEX
259 * that has the localized name.
260 *
261 * We have to get the English name because that's what the Windows registry uses
262 * for name to file mapping.
263 */
GetFullEnglishName(const ENUMLOGFONTEX * lpelfe)264 static TCHAR *GetFullEnglishName( const ENUMLOGFONTEX *lpelfe )
265 {
266
267 HFONT hFont = NULL;
268 HDC hDc = NULL;
269 FT_Byte *p_table = NULL;
270 TCHAR *psz_result = NULL;
271
272 hFont = CreateFontIndirect( &lpelfe->elfLogFont );
273
274 if( !hFont )
275 return NULL;
276
277 hDc = CreateCompatibleDC( NULL );
278
279 if( !hDc )
280 {
281 DeleteObject( hFont );
282 return NULL;
283 }
284
285 HFONT hOriginalFont = ( HFONT ) SelectObject( hDc, hFont );
286
287 const uint32_t i_name_tag = ntoh32( ( uint32_t ) 'n' << 24
288 | ( uint32_t ) 'a' << 16
289 | ( uint32_t ) 'm' << 8
290 | ( uint32_t ) 'e' << 0 );
291
292 int i_size = GetFontData( hDc, i_name_tag, 0, 0, 0 );
293
294 if( i_size <= 0 )
295 goto done;
296
297 p_table = malloc( i_size );
298
299 if( !p_table )
300 goto done;
301
302 if( GetFontData( hDc, i_name_tag, 0, p_table, i_size ) <= 0 )
303 goto done;
304
305 FT_Byte *p_name = NULL;
306 FT_UInt i_name_length = 0;
307
308 /* FIXME: Try other combinations of platform/encoding/language IDs if necessary */
309 if( GetSfntNameString( p_table, i_size, 3, 1, 4, 0x409, &p_name, &i_name_length) )
310 goto done;
311
312 int i_length_in_wchars = i_name_length / 2;
313 wchar_t *psz_name = vlc_alloc( i_length_in_wchars + 1, sizeof( *psz_name ) );
314
315 if( !psz_name )
316 goto done;
317
318 for( int i = 0; i < i_length_in_wchars; ++i )
319 psz_name[ i ] = U16_AT( p_name + i * 2 );
320 psz_name[ i_length_in_wchars ] = 0;
321
322 #ifdef UNICODE
323 psz_result = psz_name;
324 #else
325 psz_result = FromWide( psz_name );
326 free( psz_name );
327 #endif
328
329 done:
330 free( p_table );
331 SelectObject( hDc, hOriginalFont );
332 DeleteObject( hFont );
333 DeleteDC( hDc );
334
335 return psz_result;
336 }
337
EnumFontCallback(const ENUMLOGFONTEX * lpelfe,const NEWTEXTMETRICEX * metric,DWORD type,LPARAM lParam)338 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *lpelfe, const NEWTEXTMETRICEX *metric,
339 DWORD type, LPARAM lParam)
340 {
341 VLC_UNUSED( metric );
342 if( (type & RASTER_FONTTYPE) ) return 1;
343
344 vlc_family_t *p_family = ( vlc_family_t * ) lParam;
345
346 bool b_bold = ( lpelfe->elfLogFont.lfWeight == FW_BOLD );
347 bool b_italic = ( lpelfe->elfLogFont.lfItalic != 0 );
348
349 /*
350 * This function will be called by Windows as many times for each font
351 * of the family as the number of scripts the font supports.
352 * Check to avoid duplicates.
353 */
354 for( vlc_font_t *p_font = p_family->p_fonts; p_font; p_font = p_font->p_next )
355 if( !!p_font->b_bold == !!b_bold && !!p_font->b_italic == !!b_italic )
356 return 1;
357
358 char *psz_filename = NULL;
359 char *psz_fontfile = NULL;
360 int i_index = 0;
361
362 if( GetFileFontByName( (LPCTSTR)lpelfe->elfFullName, &psz_filename, &i_index ) )
363 {
364 TCHAR *psz_english_name = GetFullEnglishName( lpelfe );
365
366 if( !psz_english_name )
367 return 1;
368
369 if( GetFileFontByName( psz_english_name, &psz_filename, &i_index ) )
370 {
371 free( psz_english_name );
372 return 1;
373 }
374
375 free( psz_english_name );
376 }
377
378 if( strchr( psz_filename, DIR_SEP_CHAR ) )
379 psz_fontfile = psz_filename;
380 else
381 {
382 /* Get Windows Font folder */
383 char *psz_win_fonts_path = GetWindowsFontPath();
384 if( asprintf( &psz_fontfile, "%s\\%s", psz_win_fonts_path, psz_filename ) == -1 )
385 {
386 free( psz_filename );
387 free( psz_win_fonts_path );
388 return 1;
389 }
390 free( psz_filename );
391 free( psz_win_fonts_path );
392 }
393
394 NewFont( psz_fontfile, i_index, b_bold, b_italic, p_family );
395
396 return 1;
397 }
398
Win32_GetFamily(filter_t * p_filter,const char * psz_family)399 const vlc_family_t *Win32_GetFamily( filter_t *p_filter, const char *psz_family )
400 {
401 filter_sys_t *p_sys = p_filter->p_sys;
402 char *psz_lc = ToLower( psz_family );
403
404 if( unlikely( !psz_lc ) )
405 return NULL;
406
407 vlc_family_t *p_family =
408 vlc_dictionary_value_for_key( &p_sys->family_map, psz_lc );
409
410 free( psz_lc );
411
412 if( p_family )
413 return p_family;
414
415 p_family = NewFamily( p_filter, psz_family, &p_sys->p_families,
416 &p_sys->family_map, psz_family );
417
418 if( unlikely( !p_family ) )
419 return NULL;
420
421 LOGFONT lf;
422 lf.lfCharSet = DEFAULT_CHARSET;
423
424 LPTSTR psz_fbuffer = ToT( psz_family );
425 _tcsncpy( (LPTSTR)&lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
426 free( psz_fbuffer );
427
428 /* */
429 HDC hDC = GetDC( NULL );
430 EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)&EnumFontCallback, (LPARAM)p_family, 0);
431 ReleaseDC(NULL, hDC);
432
433 return p_family;
434 }
435
MetaFileEnumProc(HDC hdc,HANDLETABLE * table,CONST ENHMETARECORD * record,int table_entries,LPARAM log_font)436 static int CALLBACK MetaFileEnumProc( HDC hdc, HANDLETABLE* table,
437 CONST ENHMETARECORD* record,
438 int table_entries, LPARAM log_font )
439 {
440 VLC_UNUSED( hdc );
441 VLC_UNUSED( table );
442 VLC_UNUSED( table_entries );
443
444 if( record->iType == EMR_EXTCREATEFONTINDIRECTW )
445 {
446 const EMREXTCREATEFONTINDIRECTW* create_font_record =
447 ( const EMREXTCREATEFONTINDIRECTW * ) record;
448
449 *( ( LOGFONT * ) log_font ) = create_font_record->elfw.elfLogFont;
450 }
451 return 1;
452 }
453
454 /**
455 * This is a hack used by Chrome and WebKit to expose the fallback font used
456 * by Uniscribe for some given text for use with custom shapers / font engines.
457 */
UniscribeFallback(const char * psz_family,uni_char_t codepoint)458 static char *UniscribeFallback( const char *psz_family, uni_char_t codepoint )
459 {
460 HDC hdc = NULL;
461 HDC meta_file_dc = NULL;
462 HENHMETAFILE meta_file = NULL;
463 LPTSTR psz_fbuffer = NULL;
464 char *psz_result = NULL;
465
466 hdc = CreateCompatibleDC( NULL );
467 if( !hdc )
468 return NULL;
469
470 meta_file_dc = CreateEnhMetaFile( hdc, NULL, NULL, NULL );
471 if( !meta_file_dc )
472 goto error;
473
474 LOGFONT lf;
475 memset( &lf, 0, sizeof( lf ) );
476
477 psz_fbuffer = ToT( psz_family );
478 if( !psz_fbuffer )
479 goto error;
480 _tcsncpy( ( LPTSTR ) &lf.lfFaceName, psz_fbuffer, LF_FACESIZE );
481 free( psz_fbuffer );
482
483 lf.lfCharSet = DEFAULT_CHARSET;
484 HFONT hFont = CreateFontIndirect( &lf );
485 if( !hFont )
486 goto error;
487
488 HFONT hOriginalFont = SelectObject( meta_file_dc, hFont );
489
490 TCHAR text = codepoint;
491
492 SCRIPT_STRING_ANALYSIS script_analysis;
493 HRESULT hresult = ScriptStringAnalyse( meta_file_dc, &text, 1, 0, -1,
494 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
495 0, NULL, NULL, NULL, NULL, NULL, &script_analysis );
496
497 if( SUCCEEDED( hresult ) )
498 {
499 hresult = ScriptStringOut( script_analysis, 0, 0, 0, NULL, 0, 0, FALSE );
500 ScriptStringFree( &script_analysis );
501 }
502
503 SelectObject( meta_file_dc, hOriginalFont );
504 DeleteObject( hFont );
505 meta_file = CloseEnhMetaFile( meta_file_dc );
506
507 if( SUCCEEDED( hresult ) )
508 {
509 LOGFONT log_font;
510 log_font.lfFaceName[ 0 ] = 0;
511 EnumEnhMetaFile( 0, meta_file, MetaFileEnumProc, &log_font, NULL );
512 if( log_font.lfFaceName[ 0 ] )
513 psz_result = FromT( log_font.lfFaceName );
514 }
515
516 DeleteEnhMetaFile(meta_file);
517 DeleteDC( hdc );
518 return psz_result;
519
520 error:
521 if( meta_file_dc ) DeleteEnhMetaFile( CloseEnhMetaFile( meta_file_dc ) );
522 if( hdc ) DeleteDC( hdc );
523 return NULL;
524 }
525
Win32_GetFallbacks(filter_t * p_filter,const char * psz_family,uni_char_t codepoint)526 vlc_family_t *Win32_GetFallbacks( filter_t *p_filter, const char *psz_family,
527 uni_char_t codepoint )
528 {
529 vlc_family_t *p_family = NULL;
530 vlc_family_t *p_fallbacks = NULL;
531 filter_sys_t *p_sys = p_filter->p_sys;
532 char *psz_uniscribe = NULL;
533
534
535 char *psz_lc = ToLower( psz_family );
536
537 if( unlikely( !psz_lc ) )
538 return NULL;
539
540 p_fallbacks = vlc_dictionary_value_for_key( &p_sys->fallback_map, psz_lc );
541
542 if( p_fallbacks )
543 p_family = SearchFallbacks( p_filter, p_fallbacks, codepoint );
544
545 /*
546 * If the fallback list of psz_family has no family which contains the requested
547 * codepoint, try UniscribeFallback(). If it returns a valid family which does
548 * contain that codepoint, add the new family to the fallback list to speed up
549 * later searches.
550 */
551 if( !p_family )
552 {
553 psz_uniscribe = UniscribeFallback( psz_lc, codepoint );
554
555 if( !psz_uniscribe )
556 goto done;
557
558 const vlc_family_t *p_uniscribe = Win32_GetFamily( p_filter, psz_uniscribe );
559 if( !p_uniscribe || !p_uniscribe->p_fonts )
560 goto done;
561
562 FT_Face p_face = GetFace( p_filter, p_uniscribe->p_fonts );
563
564 if( !p_face || !FT_Get_Char_Index( p_face, codepoint ) )
565 goto done;
566
567 p_family = NewFamily( p_filter, psz_uniscribe, NULL, NULL, NULL );
568
569 if( unlikely( !p_family ) )
570 goto done;
571
572 p_family->p_fonts = p_uniscribe->p_fonts;
573
574 if( p_fallbacks )
575 AppendFamily( &p_fallbacks, p_family );
576 else
577 vlc_dictionary_insert( &p_sys->fallback_map,
578 psz_lc, p_family );
579 }
580
581 done:
582 free( psz_lc );
583 free( psz_uniscribe );
584 return p_family;
585 }
586
Dummy_Select(filter_t * p_filter,const char * psz_font,bool b_bold,bool b_italic,int * i_idx,uni_char_t codepoint)587 char* Dummy_Select( filter_t *p_filter, const char* psz_font,
588 bool b_bold, bool b_italic,
589 int *i_idx, uni_char_t codepoint )
590 {
591 VLC_UNUSED(p_filter);
592 VLC_UNUSED(b_bold);
593 VLC_UNUSED(b_italic);
594 VLC_UNUSED(codepoint);
595 VLC_UNUSED(i_idx);
596
597 char *psz_fontname;
598 /* Get Windows Font folder */
599 char *psz_win_fonts_path = GetWindowsFontPath();
600 if( asprintf( &psz_fontname, "%s\\%s", psz_win_fonts_path, psz_font ) == -1 )
601 psz_fontname = NULL;
602 free(psz_win_fonts_path);
603
604 return psz_fontname;
605 }
606
607 #endif
608
609