2018-02-02 03:22:14 +01:00
/*
* Copyright © 2010 Behdad Esfahbod
* Copyright © 2011 , 2012 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 , Rod Sheeter
*/
2018-01-18 07:09:07 +01:00
2021-08-10 10:21:05 +02:00
# include "batch.hh"
2021-08-12 18:39:46 +02:00
# include "face-options.hh"
2021-08-07 07:29:29 +02:00
# include "main-font-text.hh"
2021-08-12 18:39:46 +02:00
# include "output-options.hh"
2018-01-18 07:09:07 +01:00
2021-08-12 05:14:55 +02:00
# include <hb-subset.h>
2018-02-02 03:22:14 +01:00
/*
* Command line interface to the harfbuzz font subsetter .
*/
2018-01-31 04:27:11 +01:00
2021-08-12 05:14:55 +02:00
struct subset_main_t : option_parser_t , face_options_t , output_options_t < false >
2018-02-02 03:22:14 +01:00
{
2021-08-12 05:14:55 +02:00
subset_main_t ( )
: input ( hb_subset_input_create_or_fail ( ) )
{ }
~ subset_main_t ( )
{
hb_subset_input_destroy ( input ) ;
}
2021-08-12 19:38:28 +02:00
void parse_face ( int argc , const char * const * argv )
{
option_parser_t parser ;
face_options_t face_opts ;
face_opts . add_options ( & parser ) ;
GOptionEntry entries [ ] =
{
{ G_OPTION_REMAINING , 0 , G_OPTION_FLAG_IN_MAIN ,
G_OPTION_ARG_CALLBACK , ( gpointer ) & collect_face , nullptr , " [FONT-FILE] [TEXT] " } ,
{ nullptr }
} ;
parser . add_main_group ( entries , & face_opts ) ;
2021-08-31 02:35:08 +02:00
parser . add_options ( ) ;
2021-08-12 19:38:28 +02:00
g_option_context_set_ignore_unknown_options ( parser . context , true ) ;
g_option_context_set_help_enabled ( parser . context , false ) ;
2021-08-19 23:55:21 +02:00
char * * args = ( char * * )
# if GLIB_CHECK_VERSION (2, 68, 0)
g_memdup2
# else
g_memdup
# endif
( argv , argc * sizeof ( * argv ) ) ;
2021-08-29 17:44:07 +02:00
parser . parse ( & argc , & args ) ;
2021-08-12 19:38:28 +02:00
g_free ( args ) ;
set_face ( face_opts . face ) ;
}
2021-08-12 18:58:19 +02:00
void parse ( int argc , char * * argv )
2021-08-05 19:05:51 +02:00
{
2021-08-31 02:35:08 +02:00
bool help = false ;
for ( auto i = 1 ; i < argc ; i + + )
if ( ! strncmp ( " --help " , argv [ i ] , 6 ) )
{
help = true ;
break ;
}
if ( likely ( ! help ) )
{
/* Do a preliminary parse to load font-face, such that we can use it
* during main option parsing . */
parse_face ( argc , argv ) ;
}
2021-08-12 19:38:28 +02:00
2021-08-12 03:48:28 +02:00
add_options ( ) ;
2021-08-12 18:58:19 +02:00
option_parser_t : : parse ( & argc , & argv ) ;
}
int operator ( ) ( int argc , char * * argv )
{
parse ( argc , argv ) ;
2018-01-31 04:27:11 +01:00
2021-08-12 03:48:28 +02:00
hb_face_t * new_face = nullptr ;
for ( unsigned i = 0 ; i < num_iterations ; i + + )
2019-06-26 22:23:24 +02:00
{
2021-08-12 03:48:28 +02:00
hb_face_destroy ( new_face ) ;
new_face = hb_subset_or_fail ( face , input ) ;
2019-06-26 22:23:24 +02:00
}
2021-08-12 03:48:28 +02:00
bool success = new_face ;
if ( success )
{
hb_blob_t * result = hb_face_reference_blob ( new_face ) ;
write_file ( output_file , result ) ;
hb_blob_destroy ( result ) ;
}
2021-08-12 03:28:16 +02:00
2021-08-12 03:48:28 +02:00
hb_face_destroy ( new_face ) ;
2018-01-18 07:09:07 +01:00
2021-08-12 03:48:28 +02:00
return success ? 0 : 1 ;
}
2020-10-12 15:32:22 +02:00
2021-08-12 03:48:28 +02:00
bool
write_file ( const char * output_file , hb_blob_t * blob )
{
2021-08-12 04:22:03 +02:00
assert ( out_fp ) ;
2020-10-12 15:32:22 +02:00
2021-08-12 03:48:28 +02:00
unsigned int size ;
const char * data = hb_blob_get_data ( blob , & size ) ;
2020-10-12 15:32:22 +02:00
2021-08-12 03:48:28 +02:00
while ( size )
{
size_t ret = fwrite ( data , 1 , size , out_fp ) ;
2020-10-12 15:32:22 +02:00
size - = ret ;
data + = ret ;
2021-08-12 03:48:28 +02:00
if ( size & & ferror ( out_fp ) )
2020-10-12 15:32:22 +02:00
fail ( false , " Failed to write output: %s " , strerror ( errno ) ) ;
2018-02-02 02:17:36 +01:00
}
2018-02-05 23:35:32 +01:00
2018-02-04 21:31:24 +01:00
return true ;
}
2021-08-12 05:14:55 +02:00
void add_options ( ) ;
2021-08-12 03:48:28 +02:00
protected :
2021-08-12 05:14:55 +02:00
static gboolean
2021-08-12 19:38:28 +02:00
collect_face ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error ) ;
static gboolean
2021-08-12 05:14:55 +02:00
collect_rest ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error ) ;
public :
unsigned num_iterations = 1 ;
hb_subset_input_t * input = nullptr ;
} ;
static gboolean
parse_gids ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
2021-08-12 06:34:14 +02:00
GError * * error )
2021-08-12 05:14:55 +02:00
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-25 23:31:11 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-08-12 05:14:55 +02:00
hb_set_t * gids = hb_subset_input_glyph_set ( subset_main - > input ) ;
2021-08-12 03:48:28 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( gids ) ;
2021-08-25 23:31:11 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
hb_set_clear ( gids ) ;
if ( ! is_remove )
hb_set_invert ( gids ) ;
return true ;
}
2021-08-12 05:14:55 +02:00
char * s = ( char * ) arg ;
char * p ;
while ( s & & * s )
2018-02-04 21:31:24 +01:00
{
2021-08-12 05:14:55 +02:00
while ( * s & & strchr ( " , " , * s ) )
s + + ;
if ( ! * s )
break ;
2021-08-12 03:48:28 +02:00
2021-08-12 05:14:55 +02:00
errno = 0 ;
hb_codepoint_t start_code = strtoul ( s , & p , 10 ) ;
if ( s [ 0 ] = = ' - ' | | errno | | s = = p )
2021-08-05 04:23:48 +02:00
{
2021-08-12 05:14:55 +02:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing glyph-index at: '%s' " , s ) ;
return false ;
}
if ( p & & p [ 0 ] = = ' - ' ) // ranges
{
s = + + p ;
hb_codepoint_t end_code = strtoul ( s , & p , 10 ) ;
if ( s [ 0 ] = = ' - ' | | errno | | s = = p )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing glyph-index at: '%s' " , s ) ;
return false ;
}
if ( end_code < start_code )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2021-08-12 06:49:47 +02:00
" Invalid glyph-index range %u-%u " , start_code , end_code ) ;
2021-08-12 05:14:55 +02:00
return false ;
}
2021-08-25 23:31:11 +02:00
if ( ! is_remove )
hb_set_add_range ( gids , start_code , end_code ) ;
else
hb_set_del_range ( gids , start_code , end_code ) ;
2021-08-12 05:14:55 +02:00
}
else
{
2021-08-25 23:31:11 +02:00
if ( ! is_remove )
hb_set_add ( gids , start_code ) ;
else
hb_set_del ( gids , start_code ) ;
2021-08-12 05:14:55 +02:00
}
2021-08-12 06:49:47 +02:00
2021-08-12 05:14:55 +02:00
s = p ;
2021-08-12 03:48:28 +02:00
}
2021-08-12 05:14:55 +02:00
return true ;
}
2021-08-12 06:07:45 +02:00
static gboolean
parse_glyphs ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-25 23:31:11 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-08-12 20:40:21 +02:00
hb_set_t * gids = hb_subset_input_glyph_set ( subset_main - > input ) ;
2021-08-12 06:07:45 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( gids ) ;
2021-08-25 23:31:11 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
hb_set_clear ( gids ) ;
if ( ! is_remove )
hb_set_invert ( gids ) ;
return true ;
}
2021-08-12 20:40:21 +02:00
const char * p = arg ;
const char * p_end = arg + strlen ( arg ) ;
2021-08-12 06:07:45 +02:00
2021-08-12 20:40:21 +02:00
hb_font_t * font = hb_font_create ( subset_main - > face ) ;
while ( p < p_end )
{
while ( p < p_end & & ( * p = = ' ' | | * p = = ' , ' ) )
p + + ;
const char * end = p ;
while ( end < p_end & & * end ! = ' ' & & * end ! = ' , ' )
end + + ;
if ( p < end )
{
hb_codepoint_t gid ;
if ( ! hb_font_get_glyph_from_name ( font , p , end - p , & gid ) )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing glyph name: '%s' " , p ) ;
return false ;
}
2021-08-25 23:31:11 +02:00
if ( ! is_remove )
hb_set_add ( gids , gid ) ;
else
hb_set_del ( gids , gid ) ;
2021-08-12 20:40:21 +02:00
}
p = end + 1 ;
}
hb_font_destroy ( font ) ;
2021-08-12 06:07:45 +02:00
return true ;
}
2021-08-12 05:14:55 +02:00
static gboolean
parse_text ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-25 22:34:05 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-08-25 22:34:05 +02:00
hb_set_t * unicodes = hb_subset_input_unicode_set ( subset_main - > input ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( unicodes ) ;
2021-08-12 05:14:55 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
2021-08-25 22:34:05 +02:00
hb_set_clear ( unicodes ) ;
if ( ! is_remove )
hb_set_invert ( unicodes ) ;
2021-08-12 05:14:55 +02:00
return true ;
}
for ( gchar * c = ( gchar * ) arg ;
* c ;
c = g_utf8_find_next_char ( c , nullptr ) )
{
gunichar cp = g_utf8_get_char ( c ) ;
2021-08-25 22:34:05 +02:00
if ( ! is_remove )
hb_set_add ( unicodes , cp ) ;
else
hb_set_del ( unicodes , cp ) ;
2021-08-12 05:14:55 +02:00
}
return true ;
}
static gboolean
parse_unicodes ( const char * name G_GNUC_UNUSED ,
2021-08-12 03:48:28 +02:00
const char * arg ,
gpointer data ,
2021-08-12 06:34:14 +02:00
GError * * error )
2021-08-12 05:14:55 +02:00
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-25 23:31:11 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-08-25 23:31:11 +02:00
hb_set_t * unicodes = hb_subset_input_unicode_set ( subset_main - > input ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( unicodes ) ;
2021-08-12 05:14:55 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
2021-08-25 23:31:11 +02:00
hb_set_clear ( unicodes ) ;
if ( ! is_remove )
hb_set_invert ( unicodes ) ;
2021-08-12 05:14:55 +02:00
return true ;
}
2021-08-12 06:07:45 +02:00
// XXX TODO Ranges
2021-08-12 06:49:47 +02:00
# define DELIMITERS "<+->{},;&#\\xXuUnNiI\n\t\v\f\r "
char * s = ( char * ) arg ;
char * p ;
while ( s & & * s )
2021-08-12 05:14:55 +02:00
{
2021-08-12 06:49:47 +02:00
while ( * s & & strchr ( DELIMITERS , * s ) )
s + + ;
if ( ! * s )
break ;
2021-08-12 05:14:55 +02:00
2021-08-12 06:49:47 +02:00
errno = 0 ;
hb_codepoint_t start_code = strtoul ( s , & p , 16 ) ;
if ( errno | | s = = p )
2021-08-12 05:14:55 +02:00
{
2021-08-12 06:49:47 +02:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing Unicode at: '%s' " , s ) ;
return false ;
}
2021-08-12 05:14:55 +02:00
2021-08-12 06:49:47 +02:00
if ( p & & p [ 0 ] = = ' - ' ) // ranges
{
s = + + p ;
hb_codepoint_t end_code = strtoul ( s , & p , 16 ) ;
if ( s [ 0 ] = = ' - ' | | errno | | s = = p )
2021-08-12 05:14:55 +02:00
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2021-08-12 06:49:47 +02:00
" Failed parsing Unicode at: '%s' " , s ) ;
2021-08-12 05:14:55 +02:00
return false ;
}
2021-08-12 06:49:47 +02:00
if ( end_code < start_code )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Invalid Unicode range %u-%u " , start_code , end_code ) ;
return false ;
}
2021-08-25 23:31:11 +02:00
if ( ! is_remove )
hb_set_add_range ( unicodes , start_code , end_code ) ;
else
hb_set_del_range ( unicodes , start_code , end_code ) ;
2021-08-12 06:49:47 +02:00
}
else
{
2021-08-25 23:31:11 +02:00
if ( ! is_remove )
hb_set_add ( unicodes , start_code ) ;
else
hb_set_del ( unicodes , start_code ) ;
2021-08-12 05:14:55 +02:00
}
2021-08-12 06:49:47 +02:00
s = p ;
2021-08-12 05:14:55 +02:00
}
2021-08-12 06:49:47 +02:00
2021-08-12 05:14:55 +02:00
return true ;
}
static gboolean
parse_nameids ( const char * name ,
const char * arg ,
gpointer data ,
2021-08-12 06:34:14 +02:00
GError * * error )
2021-08-12 05:14:55 +02:00
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-09-16 20:23:09 +02:00
hb_set_t * name_ids = hb_subset_input_set ( subset_main - > input , HB_SUBSET_SETS_NAME_ID ) ;
2021-08-12 05:14:55 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( name_ids ) ;
2021-08-12 05:14:55 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
2021-08-12 03:48:28 +02:00
{
2021-08-26 18:51:38 +02:00
hb_set_clear ( name_ids ) ;
if ( ! is_remove )
hb_set_invert ( name_ids ) ;
2021-08-12 05:14:55 +02:00
return true ;
}
2021-08-12 03:48:28 +02:00
2021-08-12 05:14:55 +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 )
2021-08-12 03:48:28 +02:00
{
2021-08-12 05:14:55 +02:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
2021-08-12 06:49:47 +02:00
" Failed parsing nameID at: '%s' " , s ) ;
2021-08-12 05:14:55 +02:00
return false ;
2021-05-26 23:18:32 +02:00
}
2018-02-07 19:07:46 +01:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
2021-06-10 01:33:50 +02:00
{
2021-08-12 05:14:55 +02:00
hb_set_add ( name_ids , u ) ;
} else {
hb_set_del ( name_ids , u ) ;
2021-06-10 01:33:50 +02:00
}
2018-01-18 07:09:07 +01:00
2021-08-12 05:14:55 +02:00
s = p ;
2018-01-18 07:09:07 +01:00
}
2021-08-12 05:14:55 +02:00
return true ;
}
static gboolean
parse_name_languages ( const char * name ,
const char * arg ,
gpointer data ,
2021-08-12 06:34:14 +02:00
GError * * error )
2021-08-12 05:14:55 +02:00
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-09-16 20:23:09 +02:00
hb_set_t * name_languages = hb_subset_input_set ( subset_main - > input , HB_SUBSET_SETS_NAME_LANG_ID ) ;
2021-08-12 05:14:55 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( name_languages ) ;
2021-08-12 05:14:55 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
2021-08-26 18:51:38 +02:00
hb_set_clear ( name_languages ) ;
if ( ! is_remove )
hb_set_invert ( name_languages ) ;
2021-08-12 05:14:55 +02:00
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 )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing name-language code at: '%s' " , s ) ;
return false ;
}
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
2021-08-12 05:14:55 +02:00
{
hb_set_add ( name_languages , u ) ;
} else {
hb_set_del ( name_languages , u ) ;
}
s = p ;
}
return true ;
}
template < hb_subset_flags_t flag >
static gboolean
set_flag ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
hb_subset_input_set_flags ( subset_main - > input ,
hb_subset_input_get_flags ( subset_main - > input ) | flag ) ;
return true ;
}
static gboolean
parse_layout_features ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-09-16 20:23:09 +02:00
hb_set_t * layout_features = hb_subset_input_set ( subset_main - > input , HB_SUBSET_SETS_LAYOUT_FEATURE_TAG ) ;
2021-08-12 05:14:55 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( layout_features ) ;
2021-08-12 05:14:55 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
2021-08-25 00:53:32 +02:00
hb_set_clear ( layout_features ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
2021-08-25 00:53:32 +02:00
hb_set_invert ( layout_features ) ;
2021-08-12 05:14:55 +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 ,
2021-08-12 06:49:47 +02:00
" Failed parsing table tag at: '%s' " , s ) ;
2021-08-12 05:14:55 +02:00
return false ;
}
hb_tag_t tag = hb_tag_from_string ( s , strlen ( s ) ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
2021-08-12 05:14:55 +02:00
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 ,
2021-08-12 06:34:14 +02:00
GError * * error )
2021-08-12 05:14:55 +02:00
{
subset_main_t * subset_main = ( subset_main_t * ) data ;
2021-08-26 18:51:38 +02:00
hb_bool_t is_remove = ( name [ strlen ( name ) - 1 ] = = ' - ' ) ;
hb_bool_t is_add = ( name [ strlen ( name ) - 1 ] = = ' + ' ) ;
2021-09-16 20:23:09 +02:00
hb_set_t * drop_tables = hb_subset_input_set ( subset_main - > input , HB_SUBSET_SETS_DROP_TABLE_TAG ) ;
2021-08-12 05:14:55 +02:00
2021-08-26 18:51:38 +02:00
if ( ! is_remove & & ! is_add ) hb_set_clear ( drop_tables ) ;
2021-08-12 05:14:55 +02:00
2021-08-26 18:51:38 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
{
2021-08-12 05:14:55 +02:00
hb_set_clear ( drop_tables ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
hb_set_invert ( drop_tables ) ;
return true ;
}
2021-08-12 05:14:55 +02:00
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 ,
2021-08-12 06:49:47 +02:00
" Failed parsing table tag at: '%s' " , s ) ;
2021-08-12 05:14:55 +02:00
return false ;
}
hb_tag_t tag = hb_tag_from_string ( s , strlen ( s ) ) ;
2021-08-26 18:51:38 +02:00
if ( ! is_remove )
2021-08-12 05:14:55 +02:00
hb_set_add ( drop_tables , tag ) ;
else
hb_set_del ( drop_tables , tag ) ;
s = strtok ( nullptr , " , " ) ;
}
return true ;
}
2021-08-12 06:34:14 +02:00
template < GOptionArgFunc line_parser , bool allow_comments = true >
static gboolean
parse_file_for ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error )
{
FILE * fp = nullptr ;
if ( 0 ! = strcmp ( arg , " - " ) )
fp = fopen ( arg , " r " ) ;
else
fp = stdin ;
if ( ! fp )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
" Failed opening file `%s': %s " ,
arg , strerror ( errno ) ) ;
return false ;
}
GString * gs = g_string_new ( nullptr ) ;
do
{
g_string_set_size ( gs , 0 ) ;
char buf [ BUFSIZ ] ;
while ( fgets ( buf , sizeof ( buf ) , fp ) )
{
unsigned bytes = strlen ( buf ) ;
if ( bytes & & buf [ bytes - 1 ] = = ' \n ' )
{
bytes - - ;
g_string_append_len ( gs , buf , bytes ) ;
break ;
}
g_string_append_len ( gs , buf , bytes ) ;
}
if ( ferror ( fp ) )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
" Failed reading file `%s': %s " ,
arg , strerror ( errno ) ) ;
return false ;
}
g_string_append_c ( gs , ' \0 ' ) ;
if ( allow_comments )
{
char * comment = strchr ( gs - > str , ' # ' ) ;
if ( comment )
* comment = ' \0 ' ;
}
2021-08-26 22:59:29 +02:00
line_parser ( " + " , gs - > str , data , error ) ;
2021-08-12 06:34:14 +02:00
if ( * error )
break ;
}
while ( ! feof ( fp ) ) ;
g_string_free ( gs , false ) ;
return true ;
}
2021-08-12 19:38:28 +02:00
gboolean
subset_main_t : : collect_face ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error )
{
face_options_t * thiz = ( face_options_t * ) data ;
if ( ! thiz - > font_file )
{
thiz - > font_file = g_strdup ( arg ) ;
return true ;
}
return true ;
}
2021-08-12 05:14:55 +02:00
gboolean
subset_main_t : : collect_rest ( const char * name ,
const char * arg ,
gpointer data ,
GError * * error )
{
subset_main_t * thiz = ( subset_main_t * ) data ;
if ( ! thiz - > font_file )
{
thiz - > font_file = g_strdup ( arg ) ;
r eturn true ;
}
parse_text ( name , arg , data , error ) ;
return true ;
}
void
subset_main_t : : add_options ( )
{
2021-08-12 19:17:26 +02:00
set_summary ( " Subset fonts to specification. " ) ;
2021-08-12 05:14:55 +02:00
face_options_t : : add_options ( this ) ;
GOptionEntry glyphset_entries [ ] =
{
2021-08-25 23:51:21 +02:00
{ " gids " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_gids ,
" Specify glyph IDs or ranges to include in the subset. \n "
2021-09-08 15:12:52 +02:00
" "
2021-08-25 23:51:21 +02:00
" Use --gids-=... to subtract codepoints from the current set. " , " list of glyph indices/ranges or * " } ,
2021-08-25 23:31:11 +02:00
{ " gids- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_gids , " Specify glyph IDs or ranges to remove from the subset " , " list of glyph indices/ranges or * " } ,
{ " gids+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_gids , " Specify glyph IDs or ranges to include in the subset " , " list of glyph indices/ranges or * " } ,
2021-08-12 20:44:52 +02:00
{ " gids-file " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_file_for < parse_gids > , " Specify file to read glyph IDs or ranges from " , " filename " } ,
2021-08-25 23:31:11 +02:00
{ " glyphs " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_glyphs , " Specify glyph names to include in the subset. Use --glyphs-=... to subtract glyphs from the current set. " , " list of glyph names or * " } ,
{ " glyphs+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_glyphs , " Specify glyph names to include in the subset " , " list of glyph names " } ,
{ " glyphs- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_glyphs , " Specify glyph names to remove from the subset " , " list of glyph names " } ,
Fix various typos
Found via `codespell -q 3 -S ./perf/texts -L actualy,ba,beng,fo,gir,inout,nd,ot,pres,ro,te,teh,timne`
2022-01-16 13:00:53 +01:00
{ " glyphs-file " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_file_for < parse_glyphs > , " Specify file to read glyph names from " , " filename " } ,
2021-08-25 23:31:11 +02:00
{ " text " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text , " Specify text to include in the subset. Use --text-=... to subtract codepoints from the current set. " , " string " } ,
{ " text- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text , " Specify text to remove from the subset " , " string " } ,
{ " text+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text , " Specify text to include in the subset " , " string " } ,
2021-08-12 20:44:52 +02:00
{ " text-file " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_file_for < parse_text , false > , " Specify file to read text from " , " filename " } ,
2021-08-25 23:31:11 +02:00
{ " unicodes " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes ,
2021-08-25 23:51:21 +02:00
" Specify Unicode codepoints or ranges to include in the subset. Use * to include all codepoints. \n "
2021-09-08 15:12:52 +02:00
" "
" --unicodes-=... can be used to subtract codepoints from the current set. \n "
" "
2021-08-25 23:51:21 +02:00
" For example: --unicodes=* --unicodes-=41,42,43 would create a subset with all codepoints \n "
2021-09-08 15:12:52 +02:00
" "
2021-08-25 23:51:21 +02:00
" except for 41, 42, 43. " ,
2021-08-25 23:31:11 +02:00
" list of hex numbers/ranges or * " } ,
{ " unicodes- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes , " Specify Unicode codepoints or ranges to remove from the subset " , " list of hex numbers/ranges or * " } ,
{ " unicodes+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes , " Specify Unicode codepoints or ranges to include in the subset " , " list of hex numbers/ranges or * " } ,
2021-08-25 22:34:05 +02:00
2021-08-12 20:44:52 +02:00
{ " unicodes-file " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_file_for < parse_unicodes > , " Specify file to read Unicode codepoints or ranges from " , " filename " } ,
2021-08-12 05:14:55 +02:00
{ nullptr }
} ;
add_group ( glyphset_entries ,
" subset-glyphset " ,
" Subset glyph-set option: " ,
" Subsetting glyph-set options " ,
this ) ;
GOptionEntry other_entries [ ] =
{
2021-08-26 18:51:38 +02:00
{ " name-IDs " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids. Use --name-IDs-=... to substract from the current set. " , " list of int numbers or * " } ,
{ " name-IDs- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers or * " } ,
{ " name-IDs+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_nameids , " Subset specified nameids " , " list of int numbers or * " } ,
{ " name-languages " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs. Use --name-languages-=... to substract from the current set. " , " list of int numbers or * " } ,
{ " name-languages- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs " , " list of int numbers or * " } ,
{ " name-languages+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_name_languages , " Subset nameRecords with specified language IDs " , " list of int numbers or * " } ,
{ " layout-features " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved. Use --layout-features-=... to substract from the current set. " , " list of string table tags or * " } ,
{ " layout-features+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved " , " list of string table tags or * " } ,
{ " layout-features- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_layout_features , " Specify set of layout feature tags that will be preserved " , " list of string table tags or * " } ,
{ " drop-tables " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. Use --drop-tables-=... to substract from the current set. " , " list of string table tags or * " } ,
{ " drop-tables+ " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. " , " list of string table tags or * " } ,
{ " drop-tables- " , 0 , G_OPTION_FLAG_HIDDEN , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_drop_tables , " Drop the specified tables. " , " list of string table tags or * " } ,
2021-08-12 05:14:55 +02:00
{ nullptr }
} ;
add_group ( other_entries ,
" subset-other " ,
" Subset other option: " ,
" Subsetting other options " ,
this ) ;
GOptionEntry flag_entries [ ] =
{
2021-08-12 20:44:52 +02:00
{ " no-hinting " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_NO_HINTING > , " Whether to drop hints " , nullptr } ,
{ " retain-gids " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_RETAIN_GIDS > , " If set don't renumber glyph ids in the subset. " , nullptr } ,
{ " desubroutinize " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_DESUBROUTINIZE > , " Remove CFF/CFF2 use of subroutines " , nullptr } ,
{ " name-legacy " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_NAME_LEGACY > , " Keep legacy (non-Unicode) 'name' table entries " , nullptr } ,
{ " set-overlaps-flag " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG > , " Set the overlaps flag on each glyph. " , nullptr } ,
{ " notdef-outline " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_NOTDEF_OUTLINE > , " Keep the outline of \' .notdef \' glyph " , nullptr } ,
{ " no-prune-unicode-ranges " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES > , " Don't change the 'OS/2 ulUnicodeRange*' bits. " , nullptr } ,
{ " glyph-names " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_GLYPH_NAMES > , " Keep PS glyph names in TT-flavored fonts. " , nullptr } ,
2022-04-12 11:45:43 +02:00
{ " passthrough-tables " , 0 , G_OPTION_FLAG_NO_ARG , G_OPTION_ARG_CALLBACK , ( gpointer ) & set_flag < HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED > , " Do not drop tables that the tool does not know how to subset. " , nullptr } ,
2021-08-12 05:14:55 +02:00
{ nullptr }
} ;
add_group ( flag_entries ,
" subset-flags " ,
" Subset boolean option: " ,
" Subsetting boolean options " ,
this ) ;
GOptionEntry app_entries [ ] =
{
{ " num-iterations " , ' n ' , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_INT ,
& this - > num_iterations ,
" Run subsetter N times (default: 1) " , " N " } ,
{ nullptr }
} ;
add_group ( app_entries ,
" subset-app " ,
" Subset app option: " ,
" Subsetting application options " ,
this ) ;
output_options_t : : add_options ( this ) ;
GOptionEntry entries [ ] =
{
{ G_OPTION_REMAINING , 0 , G_OPTION_FLAG_IN_MAIN ,
G_OPTION_ARG_CALLBACK , ( gpointer ) & collect_rest , nullptr , " [FONT-FILE] [TEXT] " } ,
{ nullptr }
} ;
add_main_group ( entries , this ) ;
option_parser_t : : add_options ( ) ;
}
2018-02-02 03:22:14 +01:00
int
main ( int argc , char * * argv )
{
2021-08-12 03:48:28 +02:00
return batch_main < subset_main_t , true > ( argc , argv ) ;
2018-01-18 07:09:07 +01:00
}