[subset] Use hb_subset_input_t inside of subset_options_t so that input defaults are shared between the library and cli.
This commit is contained in:
parent
67064294a0
commit
0ff3618c2d
@ -47,10 +47,37 @@ hb_subset_input_create_or_fail ()
|
||||
input->name_ids = hb_set_create ();
|
||||
input->drop_tables = hb_set_create ();
|
||||
input->drop_hints = false;
|
||||
input->drop_layout = true;
|
||||
input->desubroutinize = false;
|
||||
input->retain_gids = false;
|
||||
|
||||
hb_tag_t default_drop_tables[] = {
|
||||
// Layout disabled by default
|
||||
HB_TAG ('G', 'S', 'U', 'B'),
|
||||
HB_TAG ('G', 'P', 'O', 'S'),
|
||||
HB_TAG ('G', 'D', 'E', 'F'),
|
||||
|
||||
// Copied from fontTools:
|
||||
HB_TAG ('B', 'A', 'S', 'E'),
|
||||
HB_TAG ('J', 'S', 'T', 'F'),
|
||||
HB_TAG ('D', 'S', 'I', 'G'),
|
||||
HB_TAG ('E', 'B', 'D', 'T'),
|
||||
HB_TAG ('E', 'B', 'L', 'C'),
|
||||
HB_TAG ('E', 'B', 'S', 'C'),
|
||||
HB_TAG ('S', 'V', 'G', ' '),
|
||||
HB_TAG ('P', 'C', 'L', 'T'),
|
||||
HB_TAG ('L', 'T', 'S', 'H'),
|
||||
// Graphite tables
|
||||
HB_TAG ('F', 'e', 'a', 't'),
|
||||
HB_TAG ('G', 'l', 'a', 't'),
|
||||
HB_TAG ('G', 'l', 'o', 'c'),
|
||||
HB_TAG ('S', 'i', 'l', 'f'),
|
||||
HB_TAG ('S', 'i', 'l', 'l'),
|
||||
// Colour
|
||||
HB_TAG ('s', 'b', 'i', 'x')
|
||||
};
|
||||
|
||||
input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables));
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@ -138,19 +165,6 @@ hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input)
|
||||
return subset_input->drop_hints;
|
||||
}
|
||||
|
||||
HB_EXTERN void
|
||||
hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
|
||||
hb_bool_t drop_layout)
|
||||
{
|
||||
subset_input->drop_layout = drop_layout;
|
||||
}
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input)
|
||||
{
|
||||
return subset_input->drop_layout;
|
||||
}
|
||||
|
||||
HB_EXTERN void
|
||||
hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
|
||||
hb_bool_t desubroutinize)
|
||||
|
@ -43,10 +43,9 @@ struct hb_subset_input_t
|
||||
hb_set_t *name_ids;
|
||||
hb_set_t *drop_tables;
|
||||
|
||||
bool drop_hints : 1;
|
||||
bool drop_layout : 1;
|
||||
bool desubroutinize : 1;
|
||||
bool retain_gids : 1;
|
||||
bool drop_hints;
|
||||
bool desubroutinize;
|
||||
bool retain_gids;
|
||||
/* TODO
|
||||
*
|
||||
* features
|
||||
|
@ -236,7 +236,6 @@ hb_subset_plan_create (hb_face_t *face,
|
||||
hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
|
||||
|
||||
plan->drop_hints = input->drop_hints;
|
||||
plan->drop_layout = input->drop_layout;
|
||||
plan->desubroutinize = input->desubroutinize;
|
||||
plan->retain_gids = input->retain_gids;
|
||||
plan->unicodes = hb_set_create ();
|
||||
@ -255,7 +254,7 @@ hb_subset_plan_create (hb_face_t *face,
|
||||
plan->_glyphset = _populate_gids_to_retain (face,
|
||||
input->unicodes,
|
||||
input->glyphs,
|
||||
!plan->drop_layout,
|
||||
!input->drop_tables->has (HB_OT_TAG_GSUB),
|
||||
plan->unicodes,
|
||||
plan->codepoint_to_glyph);
|
||||
|
||||
|
@ -40,7 +40,6 @@ struct hb_subset_plan_t
|
||||
hb_object_header_t header;
|
||||
|
||||
bool drop_hints : 1;
|
||||
bool drop_layout : 1;
|
||||
bool desubroutinize : 1;
|
||||
bool retain_gids : 1;
|
||||
|
||||
|
@ -249,35 +249,14 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
|
||||
case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
|
||||
return plan->drop_hints;
|
||||
|
||||
#ifdef HB_NO_SUBSET_LAYOUT
|
||||
// Drop Layout Tables if requested.
|
||||
case HB_OT_TAG_GDEF:
|
||||
case HB_OT_TAG_GPOS:
|
||||
case HB_OT_TAG_GSUB:
|
||||
#ifdef HB_NO_SUBSET_LAYOUT
|
||||
return true;
|
||||
#endif
|
||||
return plan->drop_layout;
|
||||
|
||||
// Drop these tables below by default, list pulled
|
||||
// from fontTools:
|
||||
case HB_TAG ('B', 'A', 'S', 'E'):
|
||||
case HB_TAG ('J', 'S', 'T', 'F'):
|
||||
case HB_TAG ('D', 'S', 'I', 'G'):
|
||||
case HB_TAG ('E', 'B', 'D', 'T'):
|
||||
case HB_TAG ('E', 'B', 'L', 'C'):
|
||||
case HB_TAG ('E', 'B', 'S', 'C'):
|
||||
case HB_TAG ('S', 'V', 'G', ' '):
|
||||
case HB_TAG ('P', 'C', 'L', 'T'):
|
||||
case HB_TAG ('L', 'T', 'S', 'H'):
|
||||
// Graphite tables:
|
||||
case HB_TAG ('F', 'e', 'a', 't'):
|
||||
case HB_TAG ('G', 'l', 'a', 't'):
|
||||
case HB_TAG ('G', 'l', 'o', 'c'):
|
||||
case HB_TAG ('S', 'i', 'l', 'f'):
|
||||
case HB_TAG ('S', 'i', 'l', 'l'):
|
||||
// Colour
|
||||
case HB_TAG ('s', 'b', 'i', 'x'):
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -66,12 +66,6 @@ hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input);
|
||||
|
||||
HB_EXTERN void
|
||||
hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
|
||||
hb_bool_t drop_layout);
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input);
|
||||
|
||||
HB_EXTERN void
|
||||
hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
|
||||
hb_bool_t desubroutinize);
|
||||
|
@ -137,7 +137,9 @@ test_subset_glyf_with_gsub (void)
|
||||
|
||||
input = hb_subset_test_create_input (codepoints);
|
||||
hb_set_destroy (codepoints);
|
||||
hb_subset_input_set_drop_layout (input, false);
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'S', 'U', 'B'));
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'P', 'O', 'S'));
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'D', 'E', 'F'));
|
||||
|
||||
face_subset = hb_subset_test_create_subset (face_fil, input);
|
||||
|
||||
@ -164,7 +166,9 @@ test_subset_glyf_without_gsub (void)
|
||||
|
||||
input = hb_subset_test_create_input (codepoints);
|
||||
hb_set_destroy (codepoints);
|
||||
hb_subset_input_set_drop_layout (input, true);
|
||||
hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'S', 'U', 'B'));
|
||||
hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'P', 'O', 'S'));
|
||||
hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'D', 'E', 'F'));
|
||||
|
||||
face_subset = hb_subset_test_create_subset (face_fil, input);
|
||||
|
||||
|
@ -16,10 +16,16 @@ trySubset (hb_face_t *face,
|
||||
{
|
||||
hb_subset_input_t *input = hb_subset_input_create_or_fail ();
|
||||
hb_subset_input_set_drop_hints (input, drop_hints);
|
||||
hb_subset_input_set_drop_layout (input, drop_layout);
|
||||
hb_subset_input_set_retain_gids (input, retain_gids);
|
||||
hb_set_t *codepoints = hb_subset_input_unicode_set (input);
|
||||
|
||||
if (!drop_layout)
|
||||
{
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'S', 'U', 'B'));
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'P', 'O', 'S'));
|
||||
hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'D', 'E', 'F'));
|
||||
}
|
||||
|
||||
for (int i = 0; i < text_length; i++)
|
||||
{
|
||||
hb_set_add (codepoints, text[i]);
|
||||
|
@ -50,8 +50,9 @@ bin_PROGRAMS += hb-shape
|
||||
|
||||
hb_subset_SOURCES = $(HB_SUBSET_CLI_sources)
|
||||
hb_subset_LDADD = \
|
||||
$(LDADD) \
|
||||
$(top_builddir)/src/libharfbuzz-subset.la
|
||||
$(top_builddir)/src/libharfbuzz-subset.la \
|
||||
$(LDADD)
|
||||
|
||||
bin_PROGRAMS += hb-subset
|
||||
|
||||
hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources)
|
||||
|
@ -32,6 +32,7 @@ HB_OT_SHAPE_CLOSURE_sources = \
|
||||
HB_SUBSET_CLI_sources = \
|
||||
hb-subset.cc \
|
||||
options.cc \
|
||||
options-subset.cc \
|
||||
options.hh \
|
||||
main-font-text.hh \
|
||||
$(NULL)
|
||||
|
@ -43,7 +43,7 @@ struct subset_consumer_t
|
||||
const font_options_t *font_opts)
|
||||
{
|
||||
font = hb_font_reference (font_opts->get_font ());
|
||||
input = hb_subset_input_create_or_fail ();
|
||||
input = hb_subset_input_reference (subset_options.input);
|
||||
}
|
||||
|
||||
void consume_line (const char *text,
|
||||
@ -89,13 +89,6 @@ struct subset_consumer_t
|
||||
|
||||
void finish (const font_options_t *font_opts)
|
||||
{
|
||||
hb_subset_input_set_drop_layout (input, !subset_options.keep_layout);
|
||||
hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
|
||||
hb_subset_input_set_retain_gids (input, subset_options.retain_gids);
|
||||
hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
|
||||
hb_set_set (hb_subset_input_nameid_set (input), subset_options.name_ids);
|
||||
hb_set_set (hb_subset_input_drop_tables_set (input), subset_options.drop_tables);
|
||||
|
||||
hb_face_t *face = hb_font_get_face (font);
|
||||
|
||||
hb_face_t *new_face = hb_subset (face, input);
|
||||
|
127
util/options-subset.cc
Normal file
127
util/options-subset.cc
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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_nameids (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 *name_ids = subset_opts->input->name_ids;
|
||||
|
||||
hb_set_clear (name_ids);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
hb_set_add (name_ids, u);
|
||||
|
||||
s = p;
|
||||
}
|
||||
|
||||
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(NULL, ", ");
|
||||
}
|
||||
|
||||
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},
|
||||
{"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"},
|
||||
{"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."},
|
||||
|
||||
{nullptr}
|
||||
};
|
||||
parser->add_group (entries,
|
||||
"subset",
|
||||
"Subset options:",
|
||||
"Options subsetting",
|
||||
this);
|
||||
}
|
101
util/options.cc
101
util/options.cc
@ -972,104 +972,3 @@ format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer,
|
||||
serialize_glyphs (buffer, font, output_format, format_flags, gs);
|
||||
g_string_append_c (gs, '\n');
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_nameids (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 *name_ids = hb_set_create ();
|
||||
char *s = (char *) arg;
|
||||
char *p;
|
||||
|
||||
while (s && *s)
|
||||
{
|
||||
while (*s && strchr (DELIMITERS, *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;
|
||||
}
|
||||
|
||||
hb_set_add (name_ids, u);
|
||||
|
||||
s = p;
|
||||
}
|
||||
|
||||
hb_set_t *prev = subset_opts->name_ids;
|
||||
subset_opts->name_ids = hb_set_reference (name_ids);
|
||||
hb_set_destroy (prev);
|
||||
hb_set_destroy (name_ids);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_drop_tables (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_clear (subset_opts->drop_tables);
|
||||
char *s = (char *) arg;
|
||||
|
||||
while (s && *s)
|
||||
{
|
||||
while (*s && strchr (", ", *s))
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
|
||||
char *p = s;
|
||||
while (*p && !strchr (", ", *p))
|
||||
p++;
|
||||
|
||||
if ((p - 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, p -s);
|
||||
hb_set_add (subset_opts->drop_tables, tag);
|
||||
s = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
subset_options_t::add_options (option_parser_t *parser)
|
||||
{
|
||||
GOptionEntry entries[] =
|
||||
{
|
||||
{"layout", 0, 0, G_OPTION_ARG_NONE, &this->keep_layout, "Keep OpenType Layout tables", nullptr},
|
||||
{"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr},
|
||||
{"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr},
|
||||
{"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->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"},
|
||||
{"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
|
||||
|
||||
{nullptr}
|
||||
};
|
||||
parser->add_group (entries,
|
||||
"subset",
|
||||
"Subset options:",
|
||||
"Options subsetting",
|
||||
this);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#define OPTIONS_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-subset.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
@ -673,31 +674,18 @@ struct subset_options_t : option_group_t
|
||||
{
|
||||
subset_options_t (option_parser_t *parser)
|
||||
{
|
||||
keep_layout = false;
|
||||
drop_hints = false;
|
||||
retain_gids = false;
|
||||
desubroutinize = false;
|
||||
name_ids = hb_set_create ();
|
||||
drop_tables = hb_set_create();
|
||||
|
||||
input = hb_subset_input_create_or_fail ();
|
||||
add_options (parser);
|
||||
}
|
||||
|
||||
virtual ~subset_options_t ()
|
||||
{
|
||||
hb_set_destroy (name_ids);
|
||||
hb_set_destroy (drop_tables);
|
||||
hb_subset_input_destroy (input);
|
||||
}
|
||||
|
||||
|
||||
void add_options (option_parser_t *parser);
|
||||
|
||||
hb_bool_t keep_layout;
|
||||
hb_bool_t drop_hints;
|
||||
hb_bool_t retain_gids;
|
||||
hb_bool_t desubroutinize;
|
||||
hb_set_t *name_ids;
|
||||
hb_set_t *drop_tables;
|
||||
hb_subset_input_t *input;
|
||||
};
|
||||
|
||||
/* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */
|
||||
|
Loading…
Reference in New Issue
Block a user