/*
* 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
*/
# include "options.hh"
# include "hb-subset-input.hh"
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 ;
hb_set_t * gids = subset_opts - > input - > glyphs ;
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 ,
" Failed parsing gids values at: '%s' " , s ) ;
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 )
{
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 ;
}
if ( end_code < start_code )
{
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 ;
}
hb_set_add_range ( gids , start_code , end_code ) ;
}
else
{
hb_set_add ( gids , start_code ) ;
}
s = p ;
}
return true ;
}
static gboolean
parse_nameids ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
hb_set_t * name_ids = subset_opts - > input - > name_ids ;
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 ) ;
else
hb_set_add_range ( name_ids , 0 , 0x7FFF ) ;
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_ids ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing nameID values at: '%s' " , s ) ;
return false ;
}
if ( last_name_char ! = ' - ' )
{
hb_set_add ( name_ids , u ) ;
} else {
hb_set_del ( name_ids , u ) ;
}
s = p ;
}
return true ;
}
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 ;
hb_set_t * name_languages = subset_opts - > input - > name_languages ;
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 ;
}
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 ;
hb_set_t * layout_features = subset_opts - > input - > layout_features ;
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 = = ' - ' )
hb_set_clear ( layout_features ) ;
else
subset_opts - > input - > retain_all_layout_features = true ;
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 ;
}
static gboolean
parse_drop_tables ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_options_t * subset_opts = ( subset_options_t * ) data ;
hb_set_t * drop_tables = subset_opts - > input - > drop_tables ;
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 ,
" 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 ( drop_tables , tag ) ;
else
hb_set_del ( drop_tables , tag ) ;
s = strtok ( nullptr , " , " ) ;
}
return true ;
}
void
subset_options_t : : add_options ( option_parser_t * parser )
{
GOptionEntry entries [ ] =
{
{ " no-hinting " , 0 , 0 , G_OPTION_ARG_NONE , & this - > input - > drop_hints , " Whether to drop hints " , nullptr } ,
{ " retain-gids " , 0 , 0 , G_OPTION_ARG_NONE , & this - > input - > retain_gids , " If set don't renumber glyph ids in the subset. " , nullptr } ,
{ " 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 " } ,
{ " desubroutinize " , 0 , 0 , G_OPTION_ARG_NONE , & this - > input - > desubroutinize , " Remove CFF/CFF2 use of subroutines " , nullptr } ,
{ " 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 " } ,
{ " name-IDs+ " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers " } ,
{ " name-legacy " , 0 , 0 , G_OPTION_ARG_NONE , & this - > input - > name_legacy , " Keep legacy (non-Unicode) 'name' table entries " , nullptr } ,
{ " 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 " } ,
{ " 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. " } ,
{ " 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. " } ,
{ " num-iterations " , ' n ' , 0 , G_OPTION_ARG_INT ,
& this - > num_iterations ,
" Run subsetter N times (default: 1) " , " N " } ,
{ " set-overlaps-flag " , 0 , 0 , G_OPTION_ARG_NONE , & this - > input - > overlaps_flag ,
" Set the overlaps flag on each glyph. " , nullptr } ,
{ nullptr }
} ;
parser - > add_group ( entries ,
" subset " ,
" Subset options: " ,
" Options subsetting " ,
this ) ;
}