ref: 34010f7c473330907b39bd7d55e985f407d7f87c
parent: 4fd9cc73e672de3223a44e9034f788de6641efd0
author: Werner Lemberg <[email protected]>
date: Tue Mar 14 17:50:22 EDT 2017
[sfnt] Implement PS names for font instances [3/3]. Everything is guarded with TT_CONFIG_OPTION_GX_VAR_SUPPORT. * include/freetype/internal/tttypes.h (TT_FaceRec): New fields `var_postscript_prefix' and `var_postscript_prefix_len'. * src/sfnt/sfdriver.c: Include FT_TRUETYPE_IDS_H. (sfnt_is_alphanumeric): New wrapperfunction for `ft_isalnum'. (get_win_string, get_apple_string): Remove `const' from return value. (MAX_VALUE_DESCRIPTOR_LEN, MAX_PS_NAME_LEN): New macros. (hexdigits): New array. (sfnt_get_var_ps_name): New function, implementing Adobe TechNote 5902 to construct a PS name for a variation font instance. (sfnt_get_ps_name): Call `sfnt_get_var_ps_name' for font instances. * src/sfnt/sfobjs.c (sfnt_done_face): Updated. * src/truetype/ttgxvar.c (tt_set_mm_blend): Reset `face->postscript_name' to trigger recalculation for new instance parameters.
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,30 @@
2017-03-14 Werner Lemberg <[email protected]>
+ [sfnt] Implement PS names for font instances [3/3].
+
+ Everything is guarded with TT_CONFIG_OPTION_GX_VAR_SUPPORT.
+
+ * include/freetype/internal/tttypes.h (TT_FaceRec): New fields
+ `var_postscript_prefix' and `var_postscript_prefix_len'.
+
+ * src/sfnt/sfdriver.c: Include FT_TRUETYPE_IDS_H.
+ (sfnt_is_alphanumeric): New wrapperfunction for `ft_isalnum'.
+ (get_win_string, get_apple_string): Remove `const' from return
+ value.
+ (MAX_VALUE_DESCRIPTOR_LEN, MAX_PS_NAME_LEN): New macros.
+ (hexdigits): New array.
+ (sfnt_get_var_ps_name): New function, implementing Adobe TechNote
+ 5902 to construct a PS name for a variation font instance.
+ (sfnt_get_ps_name): Call `sfnt_get_var_ps_name' for font instances.
+
+ * src/sfnt/sfobjs.c (sfnt_done_face): Updated.
+
+ * src/truetype/ttgxvar.c (tt_set_mm_blend): Reset
+ `face->postscript_name' to trigger recalculation for new instance
+ parameters.
+
+2017-03-14 Werner Lemberg <[email protected]>
+
[sfnt] Implement PS names for font instances [2/3].
* src/sfnt/sfdriver.c (fix2float) [TT_CONFIG_OPTION_GX_VAR_SUPPORT]:
@@ -80,8 +105,8 @@
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=738
* src/sfnt/sfobjs.c (sfnt_init_face): While setting number of
- instances to zero for `CFF' fonts table, ensure that there is no `glyf'
- present also (which gets priority).
+ instances to zero for `CFF' fonts table, ensure that there is no
+ `glyf' table present also (which gets priority).
2017-03-06 Werner Lemberg <[email protected]>
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -891,7 +891,7 @@
/* variation fonts only, holding the named */
/* instance index for the current face index */
/* (starting with value~1; value~0 indicates */
- /* font access without variation data). For */
+ /* font access without a named instance). For */
/* non-variation fonts, bits 16-30 are */
/* ignored. If we have the third named */
/* instance of face~4, say, `face_index' is */
@@ -3402,6 +3402,13 @@
/* <Note> */
/* The returned pointer is owned by the face and is destroyed with */
/* it. */
+ /* */
+ /* For variation fonts, this string changes if you select a different */
+ /* instance, and you have to call `FT_Get_PostScript_Name' again to */
+ /* retrieve it. FreeType follows Adobe TechNote #5902, `Generating */
+ /* PostScript Names for Fonts Using OpenType Font Variations'. */
+ /* */
+ /* http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5902.AdobePSNameGeneration.html */
/* */
FT_EXPORT( const char* )
FT_Get_Postscript_Name( FT_Face face );
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -1309,6 +1309,15 @@
/* For example, TT_FACE_FLAG_VAR_FVAR is only */
/* set if we have at least one design axis. */
/* */
+ /* var_postscript_prefix :: */
+ /* The PostScript name prefix needed for */
+ /* constructing a variation font instance's */
+ /* PS name . */
+ /* */
+ /* var_postscript_prefix_len :: */
+ /* The length of the `var_postscript_prefix' */
+ /* string. */
+ /* */
/* horz_metrics_size :: The size of the `hmtx' table. */
/* */
/* vert_metrics_size :: The size of the `vmtx' table. */
@@ -1502,6 +1511,10 @@
FT_Bool is_default_instance; /* since 2.7.1 */
FT_UInt32 variation_support; /* since 2.7.1 */
+
+ const char* var_postscript_prefix; /* since 2.7.2 */
+ FT_Int var_postscript_prefix_len; /* since 2.7.2 */
+
#endif
/* since version 2.2 */
--- a/src/sfnt/sfdriver.c
+++ b/src/sfnt/sfdriver.c
@@ -20,6 +20,7 @@
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_SFNT_H
#include FT_INTERNAL_OBJECTS_H
+#include FT_TRUETYPE_IDS_H
#include "sfdriver.h"
#include "ttload.h"
@@ -252,6 +253,18 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* Only ASCII letters and digits are taken for a variation font */
+ /* instance's PostScript name. */
+ /* */
+ /* `ft_isalnum' is a macro, but we need a function here, thus */
+ /* this definition. */
+ static int
+ sfnt_is_alphanumeric( int c )
+ {
+ return ft_isalnum( c );
+ }
+
+
/* the implementation of MurmurHash3 is taken and adapted from */
/* https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp */
@@ -451,7 +464,7 @@
(n)->encodingID == 0 && \
(n)->languageID == 0 )
- static const char*
+ static char*
get_win_string( FT_Memory memory,
FT_Stream stream,
TT_Name entry,
@@ -460,10 +473,10 @@
{
FT_Error error = FT_Err_Ok;
- const char* result;
- FT_String* r;
- FT_Char* p;
- FT_UInt len;
+ char* result;
+ FT_String* r;
+ FT_Char* p;
+ FT_UInt len;
FT_UNUSED( error );
@@ -512,7 +525,7 @@
}
- static const char*
+ static char*
get_apple_string( FT_Memory memory,
FT_Stream stream,
TT_Name entry,
@@ -521,10 +534,10 @@
{
FT_Error error = FT_Err_Ok;
- const char* result;
- FT_String* r;
- FT_Char* p;
- FT_UInt len;
+ char* result;
+ FT_String* r;
+ FT_Char* p;
+ FT_UInt len;
FT_UNUSED( error );
@@ -604,6 +617,27 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/*
+ The maximum length of an axis value descriptor.
+
+ We need 65536 different values for the decimal fraction; this fits
+ nicely into five decimal places. Consequently, it consists of
+
+ . the minus sign if the number is negative,
+ . up to five characters for the digits before the decimal point,
+ . the decimal point if there is a fractional part, and
+ . up to five characters for the digits after the decimal point.
+
+ We also need one byte for the leading `_' character and up to four
+ bytes for the axis tag.
+ */
+#define MAX_VALUE_DESCRIPTOR_LEN ( 1 + 5 + 1 + 5 + 1 + 4 )
+
+
+ /* the maximum length of PostScript font names */
+#define MAX_PS_NAME_LEN 127
+
+
+ /*
* Find the shortest decimal representation of a 16.16 fixed point
* number. The function fills `buf' with the result, returning a pointer
* to the position after the representation's last byte.
@@ -718,39 +752,302 @@
return p + 1;
}
-#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+ static const char hexdigits[16] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
static const char*
- sfnt_get_ps_name( TT_Face face )
+ sfnt_get_var_ps_name( TT_Face face )
{
- FT_Int found, win, apple;
- const char* result = NULL;
+ FT_Error error;
+ FT_Memory memory = face->root.memory;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
- if ( face->postscript_name )
- return face->postscript_name;
+ FT_UInt num_coords;
+ FT_Fixed* coords;
+ FT_MM_Var* mm_var;
- /* scan the name table to see whether we have a Postscript name here, */
- /* either in Macintosh or Windows platform encodings */
- found = sfnt_get_name_id( face, 6, &win, &apple );
+ FT_Int found, win, apple;
+ FT_UInt i, j;
- if ( found )
+ char* result = NULL;
+ char* p;
+
+
+ if ( !face->var_postscript_prefix )
{
+ FT_UInt len;
+
+
+ /* check whether we have a Variations PostScript Name Prefix */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_VARIATIONS_PREFIX,
+ &win,
+ &apple );
+ if ( !found )
+ {
+ /* otherwise use the typographic family name */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_TYPOGRAPHIC_FAMILY,
+ &win,
+ &apple );
+ }
+
+ if ( !found )
+ {
+ /* as a last resort we try the family name; note that this is */
+ /* not in the Adobe TechNote, but GX fonts (which predate the */
+ /* TechNote) benefit from this behaviour */
+ found = sfnt_get_name_id( face,
+ TT_NAME_ID_FONT_FAMILY,
+ &win,
+ &apple );
+ }
+
+ if ( !found )
+ {
+ FT_TRACE0(( "sfnt_get_var_ps_name:"
+ " Can't construct PS name prefix for font instances\n" ));
+ return NULL;
+ }
+
/* prefer Windows entries over Apple */
if ( win != -1 )
result = get_win_string( face->root.memory,
face->name_table.stream,
face->name_table.names + win,
- sfnt_is_postscript,
- 1 );
+ sfnt_is_alphanumeric,
+ 0 );
else
result = get_apple_string( face->root.memory,
face->name_table.stream,
face->name_table.names + apple,
- sfnt_is_postscript,
- 1 );
+ sfnt_is_alphanumeric,
+ 0 );
+
+ len = ft_strlen( result );
+
+ /* sanitize if necessary; we reserve space for 36 bytes (a 128bit */
+ /* checksum as a hex number, preceded by `-' and followed by three */
+ /* ASCII dots, to be used if the constructed PS name would be too */
+ /* long); this is also sufficient for a single instance */
+ if ( len > MAX_PS_NAME_LEN - ( 1 + 32 + 3 ) )
+ {
+ len = MAX_PS_NAME_LEN - ( 1 + 32 + 3 );
+ result[len] = '\0';
+
+ FT_TRACE0(( "sfnt_get_var_ps_name:"
+ " Shortening variation PS name prefix\n"
+ " "
+ " to %d characters\n", len ));
+ }
+
+ face->var_postscript_prefix = result;
+ face->var_postscript_prefix_len = len;
}
+
+ mm->get_var_blend( FT_FACE( face ),
+ &num_coords,
+ &coords,
+ NULL,
+ &mm_var );
+
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ {
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_Long instance = ( ( face->root.face_index & 0x7FFF0000L ) >> 16 ) - 1;
+ FT_UInt psid = mm_var->namedstyle[instance].psid;
+
+ char* ps_name = NULL;
+
+
+ /* try first to load the name string with index `postScriptNameID' */
+ if ( psid == 6 ||
+ ( psid > 255 && psid < 32768 ) )
+ (void)sfnt->get_name( face, (FT_UShort)psid, &ps_name );
+
+ if ( ps_name )
+ {
+ result = ps_name;
+ goto check_length;
+ }
+ else
+ {
+ /* otherwise construct a name using `subfamilyNameID' */
+ FT_UInt strid = mm_var->namedstyle[instance].strid;
+
+ char* subfamily_name;
+ char* s;
+
+
+ (void)sfnt->get_name( face, (FT_UShort)strid, &subfamily_name );
+
+ if ( !subfamily_name )
+ {
+ FT_TRACE1(( "sfnt_get_var_ps_name:"
+ " can't construct named instance PS name;\n"
+ " "
+ " trying to construct normal instance PS name\n" ));
+ goto construct_instance_name;
+ }
+
+ /* after the prefix we have character `-' followed by the */
+ /* subfamily name (using only characters a-z, A-Z, and 0-9) */
+ if ( FT_ALLOC( result, face->var_postscript_prefix_len +
+ 1 + ft_strlen( subfamily_name ) + 1 ) )
+ return NULL;
+
+ ft_strcpy( result, face->var_postscript_prefix );
+
+ p = result + face->var_postscript_prefix_len;
+ *p++ = '-';
+
+ s = subfamily_name;
+ while ( *s )
+ {
+ if ( ft_isalnum( *s ) )
+ *p++ = *s;
+ s++;
+ }
+ *p++ = '\0';
+
+ FT_FREE( subfamily_name );
+ }
+ }
+ else
+ {
+ FT_Var_Axis* axis;
+
+
+ construct_instance_name:
+ axis = mm_var->axis;
+
+ if ( FT_ALLOC( result,
+ face->var_postscript_prefix_len +
+ num_coords * MAX_VALUE_DESCRIPTOR_LEN + 1 ) )
+ return NULL;
+
+ p = result;
+
+ ft_strcpy( p, face->var_postscript_prefix );
+ p += face->var_postscript_prefix_len;
+
+ for ( i = 0; i < num_coords; i++, coords++, axis++ )
+ {
+ FT_ULong t;
+
+
+ /* omit axis value descriptor if it is identical */
+ /* to the default axis value */
+ if ( *coords == axis->def )
+ continue;
+
+ *p++ = '_';
+ p = fixed2float( *coords, p );
+
+ t = axis->tag >> 24 & 0xFF;
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = axis->tag >> 16 & 0xFF;
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = axis->tag >> 8 & 0xFF;
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ t = axis->tag & 0xFF;
+ if ( t != ' ' && ft_isalnum( t ) )
+ *p++ = t;
+ }
+ }
+
+ check_length:
+ if ( p - result > MAX_PS_NAME_LEN )
+ {
+ /* the PS name is too long; replace the part after the prefix with */
+ /* a checksum; we use MurmurHash 3 with a hash length of 128 bit */
+
+ FT_UInt32 seed = 123456789;
+
+ FT_UInt32 hash[4];
+ FT_UInt32* h;
+
+
+ murmur_hash_3_128( result, p - result, seed, hash );
+
+ p = result + face->var_postscript_prefix_len;
+ *p++ = '-';
+
+ /* we convert the hash value to hex digits from back to front */
+ p += 32 + 3;
+ h = hash + 3;
+
+ *p-- = '\0';
+ *p-- = '.';
+ *p-- = '.';
+ *p-- = '.';
+
+ for ( i = 0; i < 4; i++, h-- )
+ {
+ FT_UInt32 v = *h;
+
+
+ for ( j = 0; j < 8; j++ )
+ {
+ *p-- = hexdigits[v & 0xF];
+ v >>= 4;
+ }
+ }
+ }
+
+ return result;
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+ static const char*
+ sfnt_get_ps_name( TT_Face face )
+ {
+ FT_Int found, win, apple;
+ const char* result = NULL;
+
+
+ if ( face->postscript_name )
+ return face->postscript_name;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( face->blend )
+ {
+ face->postscript_name = sfnt_get_var_ps_name( face );
+ return face->postscript_name;
+ }
+#endif
+
+ /* scan the name table to see whether we have a Postscript name here, */
+ /* either in Macintosh or Windows platform encodings */
+ found = sfnt_get_name_id( face, TT_NAME_ID_PS_NAME, &win, &apple );
+ if ( !found )
+ return NULL;
+
+ /* prefer Windows entries over Apple */
+ if ( win != -1 )
+ result = get_win_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + win,
+ sfnt_is_postscript,
+ 1 );
+ else
+ result = get_apple_string( face->root.memory,
+ face->name_table.stream,
+ face->name_table.names + apple,
+ sfnt_is_postscript,
+ 1 );
face->postscript_name = result;
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -1755,6 +1755,7 @@
face->root.num_fixed_sizes = 0;
FT_FREE( face->postscript_name );
+ FT_FREE( face->var_postscript_prefix );
face->sfnt = NULL;
}
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -2445,6 +2445,10 @@
face->is_default_instance = is_default_instance;
+ /* enforce recomputation of the PostScript name; */
+ FT_FREE( face->postscript_name );
+ face->postscript_name = NULL;
+
Exit:
return error;
}