2019-05-18 00:30:01 +02:00
/*
* Copyright © 2019 Google , Inc .
*
* This is part of HarfBuzz , a text shaping library .
*
* Permission is hereby granted , without written agreement and without
* license or royalty fees , to use , copy , modify , and distribute this
* software and its documentation for any purpose , provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software .
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT , INDIRECT , SPECIAL , INCIDENTAL , OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION , EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE .
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE . THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN " AS IS " BASIS , AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE , SUPPORT , UPDATES , ENHANCEMENTS , OR MODIFICATIONS .
*
* Google Author ( s ) : Garret Rieger
*/
2021-08-07 07:03:30 +02:00
# ifndef SUBSET_OPTIONS_HH
# define SUBSET_OPTIONS_HH
2019-05-18 00:30:01 +02:00
# include "options.hh"
2021-08-07 07:03:30 +02:00
# include "hb-subset.h"
struct subset_options_t
{
subset_options_t ( )
: input ( hb_subset_input_create_or_fail ( ) )
{ }
~ subset_options_t ( )
{
hb_subset_input_destroy ( input ) ;
}
void add_options ( option_parser_t * parser ) ;
hb_bool_t * bool_for ( hb_subset_flags_t flag )
{
for ( unsigned i = 0 ; i < sizeof ( int ) * 8 ; i + + )
{
if ( 1u < < i = = flag )
return & flags [ i ] ;
}
return & flags [ sizeof ( int ) * 8 - 1 ] ;
}
hb_subset_input_t * get_input ( )
{
hb_subset_flags_t flags_set = HB_SUBSET_FLAGS_DEFAULT ;
for ( unsigned i = 0 ; i < sizeof ( int ) * 8 ; i + + )
{
if ( flags [ i ] )
flags_set = ( hb_subset_flags_t ) ( flags_set | ( 1u < < i ) ) ;
}
hb_subset_input_set_flags ( input , flags_set ) ;
return input ;
}
unsigned num_iterations = 1 ;
hb_subset_input_t * input = nullptr ;
hb_bool_t flags [ sizeof ( int ) * 8 ] = { 0 } ;
} ;
2019-05-18 00:30:01 +02:00
2020-04-23 00:58:41 +02:00
static gboolean
parse_gids ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
2021-08-07 06:59:50 +02:00
hb_set_t * gids = hb_subset_input_glyph_set ( subset_opts - > input ) ;
2020-04-23 00:58:41 +02:00
char * s = ( char * ) arg ;
char * p ;
while ( s & & * s )
{
while ( * s & & strchr ( " , " , * s ) )
s + + ;
if ( ! * s )
break ;
errno = 0 ;
hb_codepoint_t start_code = strtoul ( s , & p , 10 ) ;
if ( s [ 0 ] = = ' - ' | | errno | | s = = p )
{
hb_set_destroy ( gids ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2020-07-29 06:03:32 +02:00
" Failed parsing gids values at: '%s' " , s ) ;
2020-04-23 00:58:41 +02:00
return false ;
}
if ( p & & p [ 0 ] = = ' - ' ) //gid ranges
{
s = + + p ;
hb_codepoint_t end_code = strtoul ( s , & p , 10 ) ;
if ( s [ 0 ] = = ' - ' | | errno | | s = = p )
{
2020-07-29 06:12:52 +02:00
hb_set_destroy ( gids ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing gids values at: '%s' " , s ) ;
return false ;
2020-04-23 00:58:41 +02:00
}
if ( end_code < start_code )
{
2020-07-29 06:12:52 +02:00
hb_set_destroy ( gids ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Invalid gids range value %u-%u " , start_code , end_code ) ;
return false ;
2020-04-23 00:58:41 +02:00
}
hb_set_add_range ( gids , start_code , end_code ) ;
}
else
{
hb_set_add ( gids , start_code ) ;
}
s = p ;
}
return true ;
}
2019-05-18 00:30:01 +02:00
static gboolean
2019-05-14 22:55:11 +02:00
parse_nameids ( const char * name ,
2019-08-24 15:27:14 +02:00
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
2019-05-18 00:30:01 +02:00
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
2021-08-07 06:59:50 +02:00
hb_set_t * name_ids = hb_subset_input_nameid_set ( subset_opts - > input ) ;
2019-05-18 00:30:01 +02:00
2019-05-14 22:55:11 +02:00
char last_name_char = name [ strlen ( name ) - 1 ] ;
if ( last_name_char ! = ' + ' & & last_name_char ! = ' - ' )
hb_set_clear ( name_ids ) ;
if ( 0 = = strcmp ( arg , " * " ) )
{
if ( last_name_char = = ' - ' )
hb_set_del_range ( name_ids , 0 , 0x7FFF ) ;
2019-08-24 15:27:14 +02:00
else
2019-05-14 22:55:11 +02:00
hb_set_add_range ( name_ids , 0 , 0x7FFF ) ;
return true ;
}
2019-05-18 00:30:01 +02:00
char * s = ( char * ) arg ;
char * p ;
while ( s & & * s )
{
while ( * s & & strchr ( " , " , * s ) )
s + + ;
if ( ! * s )
break ;
errno = 0 ;
hb_codepoint_t u = strtoul ( s , & p , 10 ) ;
if ( errno | | s = = p )
{
hb_set_destroy ( name_ids ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2019-08-24 15:27:14 +02:00
" Failed parsing nameID values at: '%s' " , s ) ;
2019-05-18 00:30:01 +02:00
return false ;
}
2019-05-14 22:55:11 +02:00
if ( last_name_char ! = ' - ' )
{
hb_set_add ( name_ids , u ) ;
} else {
hb_set_del ( name_ids , u ) ;
}
2019-05-18 00:30:01 +02:00
s = p ;
}
return true ;
}
2020-01-21 22:37:28 +01:00
static gboolean
parse_name_languages ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
2021-08-07 06:59:50 +02:00
hb_set_t * name_languages = hb_subset_input_namelangid_set ( subset_opts - > input ) ;
2020-01-21 22:37:28 +01:00
char last_name_char = name [ strlen ( name ) - 1 ] ;
if ( last_name_char ! = ' + ' & & last_name_char ! = ' - ' )
hb_set_clear ( name_languages ) ;
if ( 0 = = strcmp ( arg , " * " ) )
{
if ( last_name_char = = ' - ' )
hb_set_del_range ( name_languages , 0 , 0x5FFF ) ;
else
hb_set_add_range ( name_languages , 0 , 0x5FFF ) ;
return true ;
}
char * s = ( char * ) arg ;
char * p ;
while ( s & & * s )
{
while ( * s & & strchr ( " , " , * s ) )
s + + ;
if ( ! * s )
break ;
errno = 0 ;
hb_codepoint_t u = strtoul ( s , & p , 10 ) ;
if ( errno | | s = = p )
{
hb_set_destroy ( name_languages ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing name_languages values at: '%s' " , s ) ;
return false ;
}
if ( last_name_char ! = ' - ' )
{
hb_set_add ( name_languages , u ) ;
} else {
hb_set_del ( name_languages , u ) ;
}
s = p ;
}
return true ;
}
2021-05-20 02:33:46 +02:00
static gboolean
parse_layout_features ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
2021-08-07 06:59:50 +02:00
hb_set_t * layout_features = hb_subset_input_layout_features_set ( subset_opts - > input ) ;
2021-05-20 02:33:46 +02:00
char last_name_char = name [ strlen ( name ) - 1 ] ;
if ( last_name_char ! = ' + ' & & last_name_char ! = ' - ' )
hb_set_clear ( layout_features ) ;
if ( 0 = = strcmp ( arg , " * " ) )
{
if ( last_name_char = = ' - ' )
2021-07-29 20:52:14 +02:00
{
2021-05-20 02:33:46 +02:00
hb_set_clear ( layout_features ) ;
2021-07-29 20:52:14 +02:00
* subset_opts - > bool_for ( HB_SUBSET_FLAGS_RETAIN_ALL_FEATURES ) = false ;
} else {
* subset_opts - > bool_for ( HB_SUBSET_FLAGS_RETAIN_ALL_FEATURES ) = true ;
}
2021-05-20 02:33:46 +02:00
return true ;
}
char * s = strtok ( ( char * ) arg , " , " ) ;
while ( s )
{
if ( strlen ( s ) > 4 ) // table tags are at most 4 bytes
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing table tag values at: '%s' " , s ) ;
return false ;
}
hb_tag_t tag = hb_tag_from_string ( s , strlen ( s ) ) ;
if ( last_name_char ! = ' - ' )
hb_set_add ( layout_features , tag ) ;
else
hb_set_del ( layout_features , tag ) ;
s = strtok ( nullptr , " , " ) ;
}
return true ;
}
2019-05-18 00:30:01 +02:00
static gboolean
parse_drop_tables ( const char * name ,
2019-08-24 15:27:14 +02:00
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
2019-05-18 00:30:01 +02:00
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
2021-08-07 06:59:50 +02:00
hb_set_t * drop_tables = hb_subset_input_drop_tables_set ( subset_opts - > input ) ;
2019-05-18 00:30:01 +02:00
char last_name_char = name [ strlen ( name ) - 1 ] ;
if ( last_name_char ! = ' + ' & & last_name_char ! = ' - ' )
hb_set_clear ( drop_tables ) ;
char * s = strtok ( ( char * ) arg , " , " ) ;
while ( s )
{
if ( strlen ( s ) > 4 ) // Table tags are at most 4 bytes.
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2019-08-24 15:27:14 +02:00
" Failed parsing table tag values at: '%s' " , s ) ;
2019-05-18 00:30:01 +02:00
return false ;
}
hb_tag_t tag = hb_tag_from_string ( s , strlen ( s ) ) ;
if ( last_name_char ! = ' - ' )
hb_set_add ( drop_tables , tag ) ;
else
hb_set_del ( drop_tables , tag ) ;
2019-06-03 10:00:25 +02:00
s = strtok ( nullptr , " , " ) ;
2019-05-18 00:30:01 +02:00
}
return true ;
}
void
subset_options_t : : add_options ( option_parser_t * parser )
{
GOptionEntry entries [ ] =
{
2021-07-29 20:52:14 +02:00
{ " no-hinting " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_NO_HINTING ) , " Whether to drop hints " , nullptr } ,
{ " retain-gids " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_RETAIN_GIDS ) , " If set don't renumber glyph ids in the subset. " , nullptr } ,
2020-04-23 00:58:41 +02:00
{ " gids " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_gids , " Specify glyph IDs or ranges to include in the subset " , " list of comma/whitespace-separated int numbers or ranges " } ,
2021-07-29 20:52:14 +02:00
{ " desubroutinize " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_DESUBROUTINIZE ) , " Remove CFF/CFF2 use of subroutines " , nullptr } ,
2019-05-18 00:30:01 +02:00
{ " name-IDs " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers " } ,
2021-05-20 02:33:46 +02:00
{ " name-IDs- " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers " } ,
{ " name-IDs+ " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers " } ,
2021-07-29 20:52:14 +02:00
{ " name-legacy " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_NAME_LEGACY ) , " Keep legacy (non-Unicode) 'name' table entries " , nullptr } ,
2020-01-21 22:37:28 +01:00
{ " name-languages " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs " , " list of int numbers " } ,
2021-05-20 02:33:46 +02:00
{ " name-languages- " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs " , " list of int numbers " } ,
{ " name-languages+ " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs " , " list of int numbers " } ,
{ " layout-features " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved " , " list of string table tags. " } ,
{ " layout-features+ " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved " , " list of string table tags. " } ,
{ " layout-features- " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved " , " list of string table tags. " } ,
2019-05-18 00:30:01 +02:00
{ " drop-tables " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. " , " list of string table tags. " } ,
{ " drop-tables+ " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. " , " list of string table tags. " } ,
{ " drop-tables- " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. " , " list of string table tags. " } ,
2021-05-26 23:18:32 +02:00
{ " num-iterations " , ' n ' , 0 , G_OPTION_ARG_INT ,
& this - > num_iterations ,
" Run subsetter N times (default: 1) " , " N " } ,
2021-07-29 20:52:14 +02:00
{ " set-overlaps-flag " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG ) ,
2021-05-19 01:54:01 +02:00
" Set the overlaps flag on each glyph. " , nullptr } ,
2021-07-29 20:52:14 +02:00
{ " notdef-outline " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_NOTDEF_OUTLINE ) , " Keep the outline of \' .notdef \' glyph " , nullptr } ,
{ " no-prune-unicode-ranges " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES ) , " Don't change the 'OS/2 ulUnicodeRange*' bits. " , nullptr } ,
{ " glyph-names " , 0 , 0 , G_OPTION_ARG_NONE , this - > bool_for ( HB_SUBSET_FLAGS_GLYPH_NAMES ) , " Keep PS glyph names in TT-flavored fonts. " , nullptr } ,
2019-05-18 00:30:01 +02:00
{ nullptr }
} ;
parser - > add_group ( entries ,
2019-08-24 15:27:14 +02:00
" subset " ,
" Subset options: " ,
" Options subsetting " ,
this ) ;
2019-05-18 00:30:01 +02:00
}
2021-08-07 07:03:30 +02:00
# endif