[vlc-devel] [PATCH] dwrite: fix family name lookup

Thomas Guillem thomas at gllm.fr
Tue Mar 27 09:16:47 CEST 2018


Hello,

Could you be a little more verbose in your commit log, otherwise it's quite hard to follow. (I had to read https://trac.videolan.org/vlc/ticket/19662#comment:8 to understand)

Otherwise, this patch looks good to me, it does fix #19662 and #20052 indeed.


On Sat, Mar 24, 2018, at 11:11, Salah-Eddin Shaban wrote:
> Close #19662
> ---
>  modules/text_renderer/freetype/fonts/dwrite.cpp | 309 +++++++++++++++++++++---
>  1 file changed, 278 insertions(+), 31 deletions(-)
> 
> diff --git a/modules/text_renderer/freetype/fonts/dwrite.cpp b/modules/
> text_renderer/freetype/fonts/dwrite.cpp
> index 018a0b38dd..4caff06d3c 100644
> --- a/modules/text_renderer/freetype/fonts/dwrite.cpp
> +++ b/modules/text_renderer/freetype/fonts/dwrite.cpp
> @@ -33,6 +33,7 @@
>  #include <wrl/client.h>
>  #include <vector>
>  #include <stdexcept>
> +#include <regex>
>  
>  #include "../platform_fonts.h"
>  
> @@ -310,38 +311,235 @@ public:
>      }
>  };
>  
> -static void DWrite_ParseFamily( IDWriteFontFamily *p_dw_family, 
> vlc_family_t *p_family, vector< FT_Stream > &streams )
> +/*
> + * The following four functions are from
> + * 
> https://msdn.microsoft.com/zh-cn/library/windows/desktop/dd941714.aspx
> + */
> +template <class T> void SafeRelease( T **ppT )
>  {
> -    for( int i = 0; i < 4; ++i )
> +    if( *ppT )
> +    {
> +        ( *ppT )->Release();
> +        *ppT = NULL;
> +    }
> +}
> +
> +/*
> + * Select a name from a list of localized names and use a simple
> + * fallback scheme if the desired locale isn't supported.
> + */
> +HRESULT GetLocalizedName( IDWriteLocalizedStrings* names, const 
> wchar_t* locale, wstring& familyName )
> +{
> +    HRESULT hr = S_OK;
> +
> +    UINT32 nameIndex;
> +    BOOL   nameExists;
> +    UINT32 nameLength = 0;
> +
> +    try
> +    {
> +        hr = names->FindLocaleName( locale, &nameIndex, &nameExists );
> +
> +        // If there is no name with the desired locale fallback to US 
> English.
> +        if( SUCCEEDED( hr ) )
> +        {
> +            if( !nameExists )
> +                hr = names->FindLocaleName( L"en-us", &nameIndex, 
> &nameExists );
> +
> +            // If the name still doesn't exist just take the first one.
> +            if( !nameExists )
> +                nameIndex = 0;
> +        }
> +
> +        if( SUCCEEDED( hr ) )
> +        {
> +            hr = names->GetStringLength( nameIndex, &nameLength );
> +        }
> +
> +        if( SUCCEEDED( hr ) )
> +        {
> +            familyName.resize( nameLength + 1 );
> +            hr = names->GetString( nameIndex, &familyName[ 0 ], 
> ( UINT32 ) familyName.size() );
> +        }
> +    }
> +    catch( ... )
> +    {
> +        hr = E_FAIL;
> +    }
> +
> +    return hr;
> +}
> +
> +
> +/*
> + * Get the family name (e.g. "Arial" in "Arial Bold") for a font family
> + */
> +HRESULT GetFontFamilyName( IDWriteFontFamily* fontFamily, const 
> wchar_t* locale, wstring& familyName )
> +{
> +    HRESULT hr = S_OK;
> +
> +    IDWriteLocalizedStrings* familyNames = NULL;
> +
> +    hr = fontFamily->GetFamilyNames( &familyNames );
> +
> +    if( SUCCEEDED( hr ) )
> +    {
> +        hr = GetLocalizedName( familyNames, locale, familyName );
> +    }
> +
> +    SafeRelease( &familyNames );
> +
> +    return hr;
> +}
> +
> +/*
> + * Get the face name (e.g. "Bold" in "Arial Bold") for a font
> + */
> +HRESULT GetFontFaceName( IDWriteFont* font, const wchar_t* locale, 
> wstring& faceName )
> +{
> +    HRESULT hr = S_OK;
> +
> +    IDWriteLocalizedStrings* faceNames = NULL;
> +
> +    hr = font->GetFaceNames( &faceNames );
> +
> +    if( SUCCEEDED( hr ) )
> +    {
> +        hr = GetLocalizedName( faceNames, locale, faceName );
> +    }
> +
> +    SafeRelease( &faceNames );
> +
> +    return hr;
> +}
> +
> +/*
> + * Remove any extra null characters and escape any regex metacharacters
> + */
> +static wstring SanitizeName( const wstring &name )
> +{
> +    const auto pattern = wregex{ L"[.^$|()\\[\\]{}*+?\\\\]" };
> +    const auto replace = wstring{ L"\\\\&" };
> +    auto result = regex_replace( wstring{ name.c_str() }, pattern, 
> replace,
> +                                 regex_constants::match_default | 
> regex_constants::format_sed );
> +    return result;
> +}
> +
> +/*
> + * Check for a partial match e.g. between Roboto and Roboto Thin.
> + * In this case p_matched will be set to Roboto and p_unmatched to 
> Thin.
> + */
> +static bool DWrite_PartialMatch( const wstring &full_name, const 
> wstring &partial_name,
> +                                 wstring *p_matched = nullptr, wstring 
> *p_unmatched = nullptr )
> +{
> +    auto pattern = wstring{ L"^(" } + SanitizeName( partial_name ) + 
> wstring{ L")\\s*(.*)$" };
> +    auto rx = wregex{ pattern, wregex::icase };
> +
> +    auto match = wsmatch{};
> +
> +    if( !regex_match( full_name, match, rx ) )
> +        return false;
> +
> +    if( p_matched )
> +        *p_matched = match[ 1 ].str();
> +    if( p_unmatched )
> +        *p_unmatched = match[ 2 ].str();
> +    return true;
> +}
> +
> +static vector< ComPtr< IDWriteFont > > 
> DWrite_GetFonts( IDWriteFontFamily *p_dw_family, const wstring 
> &face_name )
> +{
> +    vector< ComPtr< IDWriteFont > > result;
> +    wchar_t buff_sys[ LOCALE_NAME_MAX_LENGTH ] = {};
> +    wchar_t buff_usr[ LOCALE_NAME_MAX_LENGTH ] = {};
> +
> +    GetSystemDefaultLocaleName( buff_sys, LOCALE_NAME_MAX_LENGTH );
> +    GetUserDefaultLocaleName( buff_usr, LOCALE_NAME_MAX_LENGTH );
> +
> +    if( !face_name.empty() )
> +    {
> +        UINT32 i_count = p_dw_family->GetFontCount();
> +        for( UINT32 i = 0; i < i_count; ++i )
> +        {
> +            ComPtr< IDWriteFont > p_dw_font;
> +            wstring name_en;
> +            wstring name_sys;
> +            wstring name_usr;
> +
> +            if( p_dw_family->GetFont( i, p_dw_font.GetAddressOf() )
> +                || !p_dw_font )
> +                throw runtime_error( "GetFont() failed" );
> +
> +            if( GetFontFaceName( p_dw_font.Get(), L"en-US", name_en )
> +             || GetFontFaceName( p_dw_font.Get(), buff_sys, name_sys )
> +             || GetFontFaceName( p_dw_font.Get(), buff_usr, 
> name_usr ) )
> +                throw runtime_error( "GetFontFaceName() failed" );
> +
> +            if( DWrite_PartialMatch( name_en, face_name ) ||
> +                DWrite_PartialMatch( name_sys, face_name ) ||
> +                DWrite_PartialMatch( name_usr, face_name ) )
> +                result.push_back( p_dw_font );
> +        }
> +    }
> +
> +    if( face_name.empty() || result.empty() )
> +    {
> +        for( int i = 0; i < 4; ++i )
> +        {
> +            ComPtr< IDWriteFont > p_dw_font;
> +            DWRITE_FONT_STYLE style;
> +            DWRITE_FONT_WEIGHT weight;
> +
> +            switch( i )
> +            {
> +            case 0:
> +                weight = DWRITE_FONT_WEIGHT_NORMAL; style = 
> DWRITE_FONT_STYLE_NORMAL;
> +                break;
> +            case 1:
> +                weight = DWRITE_FONT_WEIGHT_BOLD; style = 
> DWRITE_FONT_STYLE_NORMAL;
> +                break;
> +            case 2:
> +                weight = DWRITE_FONT_WEIGHT_NORMAL; style = 
> DWRITE_FONT_STYLE_ITALIC;
> +                break;
> +            default:
> +                weight = DWRITE_FONT_WEIGHT_BOLD; style = 
> DWRITE_FONT_STYLE_ITALIC;
> +                break;
> +            }
> +
> +            if( p_dw_family->GetFirstMatchingFont( weight, 
> DWRITE_FONT_STRETCH_NORMAL, style, p_dw_font.GetAddressOf() )
> +                || !p_dw_font )
> +                throw runtime_error( "GetFirstMatchingFont() failed" );
> +
> +            result.push_back( p_dw_font );
> +        }
> +    }
> +
> +    return result;
> +}
> +
> +static void DWrite_ParseFamily( IDWriteFontFamily *p_dw_family, const 
> wstring &face_name,
> +                                vlc_family_t *p_family, vector< 
> FT_Stream > &streams )
> +{
> +    vector< ComPtr< IDWriteFont > > fonts = 
> DWrite_GetFonts( p_dw_family, face_name );
> +    bool b_styles[ 2 ][ 2 ] = { false };
> +
> +    for( size_t i = 0; i < fonts.size(); ++i )
>      {
>          ComPtr< IDWriteFont >             p_dw_font;
>          ComPtr< IDWriteFontFace >         p_dw_face;
>          ComPtr< IDWriteFontFileLoader >   p_dw_loader;
>          ComPtr< IDWriteFontFile >         p_dw_file;
>  
> -        DWRITE_FONT_STYLE style;
> -        DWRITE_FONT_WEIGHT weight;
> -        bool b_bold, b_italic;
> +        p_dw_font = fonts[ i ];
>  
> -        switch( i )
> -        {
> -        case 0:
> -            weight = DWRITE_FONT_WEIGHT_NORMAL; style = 
> DWRITE_FONT_STYLE_NORMAL;
> -            b_bold = false; b_italic = false; break;
> -        case 1:
> -            weight = DWRITE_FONT_WEIGHT_BOLD; style = 
> DWRITE_FONT_STYLE_NORMAL;
> -            b_bold = true; b_italic = false; break;
> -        case 2:
> -            weight = DWRITE_FONT_WEIGHT_NORMAL; style = 
> DWRITE_FONT_STYLE_ITALIC;
> -            b_bold = false; b_italic = true; break;
> -        case 3:
> -            weight = DWRITE_FONT_WEIGHT_BOLD; style = 
> DWRITE_FONT_STYLE_ITALIC;
> -            b_bold = true; b_italic = true; break;
> -        }
> +        bool b_bold = p_dw_font->GetWeight() >= 700,
> +            b_italic = ( p_dw_font->GetStyle() == 
> DWRITE_FONT_STYLE_ITALIC ||
> +                p_dw_font->GetStyle() == DWRITE_FONT_STYLE_OBLIQUE );
>  
> -        if( p_dw_family->GetFirstMatchingFont( weight, 
> DWRITE_FONT_STRETCH_NORMAL, style, p_dw_font.GetAddressOf() )
> -         || !p_dw_font )
> -            throw runtime_error( "GetFirstMatchingFont() failed" );
> +        //avoid duplicates
> +        bool &b_added = b_styles[ b_bold ? 1 : 0 ][ b_italic ? 1 : 0 ];
> +        if( b_added )
> +            continue;
>  
>          if( p_dw_font->CreateFontFace( p_dw_face.GetAddressOf() ) )
>              throw runtime_error( "CreateFontFace() failed" );
> @@ -388,6 +586,7 @@ static void DWrite_ParseFamily( IDWriteFontFamily 
> *p_dw_family, vlc_family_t *p_
>          if( asprintf( &psz_font_path, ":dw/%d", ( int ) streams.size() 
> - 1 ) < 0 )
>              throw bad_alloc();
>  
> +        b_added = true;
>          NewFont( psz_font_path, i_font_index, b_bold, b_italic, 
> p_family );
>      }
>  }
> @@ -398,6 +597,10 @@ extern "C" const vlc_family_t 
> *DWrite_GetFamily( filter_t *p_filter, const char
>      dw_sys_t                     *p_dw_sys     = ( dw_sys_t * ) p_sys-
> >p_dw_sys;
>      ComPtr< IDWriteFontFamily >   p_dw_family;
>  
> +    UINT32 i_index;
> +    BOOL b_exists = false;
> +    wstring family_name, face_name;
> +
>      char *psz_lc = ToLower( psz_family );
>      if( unlikely( !psz_lc ) )
>          return NULL;
> @@ -422,24 +625,68 @@ extern "C" const vlc_family_t 
> *DWrite_GetFamily( filter_t *p_filter, const char
>      if( unlikely( !pwsz_family ) )
>          goto done;
>  
> -    UINT32 i_index;
> -    BOOL b_exists;
> +    /* Try to find an exact match first */
> +    if( !p_dw_sys->p_dw_system_fonts->FindFamilyName( pwsz_family, 
> &i_index, &b_exists ) && b_exists )
> +    {
> +        if( p_dw_sys->p_dw_system_fonts->GetFontFamily( i_index, 
> p_dw_family.GetAddressOf() ) )
> +        {
> +            msg_Warn( p_filter, "DWrite_GetFamily: GetFontFamily() 
> failed" );
> +        }
> +    }
>  
> -    if( p_dw_sys->p_dw_system_fonts->FindFamilyName( pwsz_family, 
> &i_index, &b_exists ) || !b_exists )
> +    /*
> +     * Try to find a partial match e.g between Roboto and Roboto Thin.
> +     * If found we search the Roboto family for faces starting with 
> Thin (Thin, Thin Italic etc)
> +     */
> +    if( !p_dw_family )
>      {
> -        msg_Warn( p_filter, "DWrite_GetFamily: FindFamilyName() 
> failed" );
> -        goto done;
> +        UINT32 i_count = p_dw_sys->p_dw_system_fonts-
> >GetFontFamilyCount();
> +        wchar_t buff_sys[ LOCALE_NAME_MAX_LENGTH ] = {};
> +        wchar_t buff_usr[ LOCALE_NAME_MAX_LENGTH ] = {};
> +
> +        GetSystemDefaultLocaleName( buff_sys, LOCALE_NAME_MAX_LENGTH );
> +        GetUserDefaultLocaleName( buff_usr, LOCALE_NAME_MAX_LENGTH );
> +
> +        for( i_index = 0; i_index < i_count; ++i_index )
> +        {
> +            ComPtr< IDWriteFontFamily > p_cur_family;
> +            if( p_dw_sys->p_dw_system_fonts->GetFontFamily( i_index, 
> p_cur_family.GetAddressOf() ) )
> +            {
> +                msg_Warn( p_filter, "DWrite_GetFamily: GetFontFamily() 
> failed" );
> +                continue;
> +            }
> +
> +            wstring name_en, name_sys, name_usr;
> +
> +            if( GetFontFamilyName( p_cur_family.Get(), L"en-US", 
> name_en )
> +             || GetFontFamilyName( p_cur_family.Get(), buff_sys, 
> name_sys )
> +             || GetFontFamilyName( p_cur_family.Get(), buff_usr, 
> name_usr ) )
> +            {
> +                msg_Warn( p_filter, "DWrite_GetFamily: 
> GetFontFamilyName() failed" );
> +                continue;
> +            }
> +
> +            wstring requested_name{ pwsz_family };
> +
> +            if( !DWrite_PartialMatch( requested_name, name_en, 
> &family_name, &face_name ) &&
> +                !DWrite_PartialMatch( requested_name, name_sys, 
> &family_name, &face_name ) &&
> +                !DWrite_PartialMatch( requested_name, name_usr, 
> &family_name, &face_name ) )
> +                continue;
> +
> +            p_dw_family = p_cur_family;
> +            break;
> +        }
>      }
>  
> -    if( p_dw_sys->p_dw_system_fonts->GetFontFamily( i_index, 
> p_dw_family.GetAddressOf() ) )
> +    if( !p_dw_family )
>      {
> -        msg_Err( p_filter, "DWrite_GetFamily: GetFontFamily() 
> failed" );
> +        msg_Warn( p_filter, "DWrite_GetFamily: family not found" );
>          goto done;
>      }
>  
>      try
>      {
> -        DWrite_ParseFamily( p_dw_family.Get(), p_family, p_dw_sys-
> >streams );
> +        DWrite_ParseFamily( p_dw_family.Get(), face_name, p_family, 
> p_dw_sys->streams );
>      }
>      catch( const exception &e )
>      {
> -- 
> 2.13.6
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list