[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.
This commit is contained in:
parent
4fd9cc73e6
commit
34010f7c47
29
ChangeLog
29
ChangeLog
|
@ -1,3 +1,28 @@
|
|||
2017-03-14 Werner Lemberg <wl@gnu.org>
|
||||
|
||||
[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 <wl@gnu.org>
|
||||
|
||||
[sfnt] Implement PS names for font instances [2/3].
|
||||
|
@ -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 <wl@gnu.org>
|
||||
|
||||
|
|
|
@ -891,7 +891,7 @@ FT_BEGIN_HEADER
|
|||
/* 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 */
|
||||
|
@ -3403,6 +3403,13 @@ FT_BEGIN_HEADER
|
|||
/* 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 );
|
||||
|
||||
|
|
|
@ -1309,6 +1309,15 @@ FT_BEGIN_HEADER
|
|||
/* 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_BEGIN_HEADER
|
|||
|
||||
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 */
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
@ -603,6 +616,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
|
||||
|
@ -718,6 +752,262 @@
|
|||
return p + 1;
|
||||
}
|
||||
|
||||
|
||||
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_var_ps_name( TT_Face face )
|
||||
{
|
||||
FT_Error error;
|
||||
FT_Memory memory = face->root.memory;
|
||||
|
||||
FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
|
||||
|
||||
FT_UInt num_coords;
|
||||
FT_Fixed* coords;
|
||||
FT_MM_Var* mm_var;
|
||||
|
||||
FT_Int found, win, apple;
|
||||
FT_UInt i, j;
|
||||
|
||||
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_alphanumeric,
|
||||
0 );
|
||||
else
|
||||
result = get_apple_string( face->root.memory,
|
||||
face->name_table.stream,
|
||||
face->name_table.names + apple,
|
||||
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 */
|
||||
|
||||
|
||||
|
@ -731,26 +1021,33 @@
|
|||
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, 6, &win, &apple );
|
||||
found = sfnt_get_name_id( face, TT_NAME_ID_PS_NAME, &win, &apple );
|
||||
if ( !found )
|
||||
return NULL;
|
||||
|
||||
if ( found )
|
||||
{
|
||||
/* prefer Windows entries over Apple */
|
||||
if ( win != -1 )
|
||||
result = get_win_string( face->root.memory,
|
||||
/* 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 + win,
|
||||
face->name_table.names + apple,
|
||||
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;
|
||||
|
||||
|
|
|
@ -1755,6 +1755,7 @@
|
|||
face->root.num_fixed_sizes = 0;
|
||||
|
||||
FT_FREE( face->postscript_name );
|
||||
FT_FREE( face->var_postscript_prefix );
|
||||
|
||||
face->sfnt = NULL;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue