@ -3,7 +3,6 @@
# include <pango/pango.h>
# ifdef __APPLE__
# include <CoreFoundation/CoreFoundation.h>
# include <CoreText/CoreText.h>
# elif defined(_WIN32)
# include <windows.h>
@ -11,8 +10,186 @@
# include <fontconfig/fontconfig.h>
# endif
# include <ft2build.h>
# include FT_FREETYPE_H
# include FT_TRUETYPE_TABLES_H
# include FT_SFNT_NAMES_H
# include FT_TRUETYPE_IDS_H
# ifndef FT_SFNT_OS2
# define FT_SFNT_OS2 ft_sfnt_os2
# endif
// OSX seems to read the strings in MacRoman encoding and ignore Unicode entries.
// You can verify this by opening a TTF with both Unicode and Macroman on OSX.
// It uses the MacRoman name, while Fontconfig and Windows use Unicode
# ifdef __APPLE__
# define PREFERRED_PLATFORM_ID TT_PLATFORM_MACINTOSH
# define PREFERRED_ENCODING_ID TT_MAC_ID_ROMAN
# else
# define PREFERRED_PLATFORM_ID TT_PLATFORM_MICROSOFT
# define PREFERRED_ENCODING_ID TT_MS_ID_UNICODE_CS
# endif
# define IS_PREFERRED_ENC(X) \
X . platform_id = = PREFERRED_PLATFORM_ID & & X . encoding_id = = PREFERRED_ENCODING_ID
/*
* Return a UTF - 8 encoded string given a TrueType name buf + len
* and its platform and encoding
*/
char *
to_utf8 ( FT_Byte * buf , FT_UInt len , FT_UShort pid , FT_UShort eid ) {
size_t ret_len = len * 4 ; // max chars in a utf8 string
char * ret = ( char * ) malloc ( ret_len + 1 ) ; // utf8 string + null
if ( ! ret ) return NULL ;
// In my testing of hundreds of fonts from the Google Font repo, the two types
// of fonts are TT_PLATFORM_MICROSOFT with TT_MS_ID_UNICODE_CS encoding, or
// TT_PLATFORM_MACINTOSH with TT_MAC_ID_ROMAN encoding. Usually both, never neither
char const * fromcode ;
if ( pid = = TT_PLATFORM_MACINTOSH & & eid = = TT_MAC_ID_ROMAN ) {
fromcode = " MAC " ;
} else if ( pid = = TT_PLATFORM_MICROSOFT & & eid = = TT_MS_ID_UNICODE_CS ) {
fromcode = " UTF-16BE " ;
} else {
free ( ret ) ;
return NULL ;
}
GIConv cd = g_iconv_open ( " UTF-8 " , fromcode ) ;
if ( cd = = ( GIConv ) - 1 ) {
free ( ret ) ;
return NULL ;
}
size_t inbytesleft = len ;
size_t outbytesleft = ret_len ;
size_t n_converted = g_iconv ( cd , ( char * * ) & buf , & inbytesleft , & ret , & outbytesleft ) ;
ret - = ret_len - outbytesleft ; // rewind the pointers to their
buf - = len - inbytesleft ; // original starting positions
if ( n_converted = = ( size_t ) - 1 ) {
free ( ret ) ;
return NULL ;
} else {
ret [ ret_len - outbytesleft ] = ' \0 ' ;
return ret ;
}
}
/*
* Find a family name in the face ' s name table , preferring the one the
* system , fall back to the other
*/
char *
get_family_name ( FT_Face face ) {
FT_SfntName name ;
char * utf8name = NULL ;
for ( unsigned i = 0 ; i < FT_Get_Sfnt_Name_Count ( face ) ; + + i ) {
FT_Get_Sfnt_Name ( face , i , & name ) ;
if ( name . name_id = = TT_NAME_ID_FONT_FAMILY ) {
char * utf8candidate = to_utf8 ( name . string , name . string_len , name . platform_id , name . encoding_id ) ;
if ( utf8candidate ) {
if ( utf8name ) free ( utf8name ) ;
if ( IS_PREFERRED_ENC ( name ) ) {
return utf8candidate ;
} else {
utf8name = utf8candidate ;
}
}
}
}
return utf8name ;
}
PangoWeight
get_pango_weight ( FT_UShort weight ) {
switch ( weight ) {
case 100 : return PANGO_WEIGHT_THIN ;
case 200 : return PANGO_WEIGHT_ULTRALIGHT ;
case 300 : return PANGO_WEIGHT_LIGHT ;
case 350 : return PANGO_WEIGHT_SEMILIGHT ;
case 380 : return PANGO_WEIGHT_BOOK ;
case 400 : return PANGO_WEIGHT_NORMAL ;
case 500 : return PANGO_WEIGHT_MEDIUM ;
case 600 : return PANGO_WEIGHT_SEMIBOLD ;
case 700 : return PANGO_WEIGHT_BOLD ;
case 800 : return PANGO_WEIGHT_ULTRABOLD ;
case 900 : return PANGO_WEIGHT_HEAVY ;
case 1000 : return PANGO_WEIGHT_ULTRAHEAVY ;
default : return PANGO_WEIGHT_NORMAL ;
}
}
PangoStretch
get_pango_stretch ( FT_UShort width ) {
switch ( width ) {
case 1 : return PANGO_STRETCH_ULTRA_CONDENSED ;
case 2 : return PANGO_STRETCH_EXTRA_CONDENSED ;
case 3 : return PANGO_STRETCH_CONDENSED ;
case 4 : return PANGO_STRETCH_SEMI_CONDENSED ;
case 5 : return PANGO_STRETCH_NORMAL ;
case 6 : return PANGO_STRETCH_SEMI_EXPANDED ;
case 7 : return PANGO_STRETCH_EXPANDED ;
case 8 : return PANGO_STRETCH_EXTRA_EXPANDED ;
case 9 : return PANGO_STRETCH_ULTRA_EXPANDED ;
default : return PANGO_STRETCH_NORMAL ;
}
}
PangoStyle
get_pango_style ( FT_Long flags ) {
if ( flags & FT_STYLE_FLAG_ITALIC ) {
return PANGO_STYLE_ITALIC ;
} else {
return PANGO_STYLE_NORMAL ;
}
}
PangoFontDescription *
get_pango_font_description ( unsigned char * filepath ) {
FT_Library library ;
FT_Face face ;
PangoFontDescription * desc = pango_font_description_new ( ) ;
if ( ! FT_Init_FreeType ( & library ) & & ! FT_New_Face ( library , ( const char * ) filepath , 0 , & face ) ) {
TT_OS2 * table = ( TT_OS2 * ) FT_Get_Sfnt_Table ( face , FT_SFNT_OS2 ) ;
char * family = get_family_name ( face ) ;
if ( family ) pango_font_description_set_family_static ( desc , family ) ;
pango_font_description_set_weight ( desc , get_pango_weight ( table - > usWeightClass ) ) ;
pango_font_description_set_stretch ( desc , get_pango_stretch ( table - > usWidthClass ) ) ;
pango_font_description_set_style ( desc , get_pango_style ( face - > style_flags ) ) ;
FT_Done_Face ( face ) ;
return desc ;
}
pango_font_description_free ( desc ) ;
return NULL ;
}
/*
* Register font with the OS
*/
bool
register_font ( unsigned char * filepath ) {
register_font ( unsigned char * filepath , PangoFontDescription * * desc ) {
bool success ;
# ifdef __APPLE__
@ -23,9 +200,11 @@ register_font(unsigned char *filepath) {
# else
success = FcConfigAppFontAddFile ( FcConfigGetCurrent ( ) , ( FcChar8 * ) ( filepath ) ) ;
# endif
if ( ! success ) return false ;
* desc = get_pango_font_description ( filepath ) ;
// Tell Pango to throw away the current FontMap and create a new one. This
// has the effect of registering the new font in Pango by re-looking up all
// font families.