mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-13 00:32:59 +00:00
Merge pull request #4221 from googlefonts/user_glyph_map
[subset] Add API method to allow a custom glyph map to be specified.
This commit is contained in:
commit
62bc2841d9
12 changed files with 174 additions and 9 deletions
|
@ -875,6 +875,7 @@ hb_subset_input_get_flags
|
|||
hb_subset_input_unicode_set
|
||||
hb_subset_input_glyph_set
|
||||
hb_subset_input_set
|
||||
hb_subset_input_old_to_new_glyph_mapping
|
||||
hb_subset_input_pin_axis_location
|
||||
hb_subset_input_pin_axis_to_default
|
||||
hb_subset_or_fail
|
||||
|
|
|
@ -520,6 +520,37 @@ hb_subset_preprocess (hb_face_t *source)
|
|||
return new_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_subset_input_old_to_new_glyph_mapping:
|
||||
* @input: a #hb_subset_input_t object.
|
||||
*
|
||||
* Returns a map which can be used to provide an explicit mapping from old to new glyph
|
||||
* id's in the produced subset. The caller should populate the map as desired.
|
||||
* If this map is left empty then glyph ids will be automatically mapped to new
|
||||
* values by the subsetter. If populated, the mapping must be unique. That
|
||||
* is no two original glyph ids can be mapped to the same new id.
|
||||
* Additionally, if a mapping is provided then the retain gids option cannot
|
||||
* be enabled.
|
||||
*
|
||||
* Any glyphs that are retained in the subset which are not specified
|
||||
* in this mapping will be assigned glyph ids after the highest glyph
|
||||
* id in the mapping.
|
||||
*
|
||||
* Note: this will accept and apply non-monotonic mappings, however this
|
||||
* may result in unsorted Coverage tables. Such fonts may not work for all
|
||||
* use cases (for example ots will reject unsorted coverage tables). So it's
|
||||
* recommended, if possible, to supply a monotonic mapping.
|
||||
*
|
||||
* Return value: (transfer none): pointer to the #hb_map_t of the custom glyphs ID map.
|
||||
*
|
||||
* XSince: REPLACEME
|
||||
**/
|
||||
HB_EXTERN hb_map_t*
|
||||
hb_subset_input_old_to_new_glyph_mapping (hb_subset_input_t *input)
|
||||
{
|
||||
return &input->glyph_map;
|
||||
}
|
||||
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
/**
|
||||
* hb_subset_input_override_name_table:
|
||||
|
|
|
@ -119,6 +119,7 @@ struct hb_subset_input_t
|
|||
bool force_long_loca = false;
|
||||
|
||||
hb_hashmap_t<hb_tag_t, float> axes_location;
|
||||
hb_map_t glyph_map;
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> name_table_overrides;
|
||||
#endif
|
||||
|
|
|
@ -768,10 +768,11 @@ _create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
|
|||
;
|
||||
}
|
||||
|
||||
static void
|
||||
static bool
|
||||
_create_old_gid_to_new_gid_map (const hb_face_t *face,
|
||||
bool retain_gids,
|
||||
const hb_set_t *all_gids_to_retain,
|
||||
const hb_map_t *requested_glyph_map,
|
||||
hb_map_t *glyph_map, /* OUT */
|
||||
hb_map_t *reverse_glyph_map, /* OUT */
|
||||
unsigned int *num_glyphs /* OUT */)
|
||||
|
@ -780,7 +781,54 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
|
|||
reverse_glyph_map->resize (pop);
|
||||
glyph_map->resize (pop);
|
||||
|
||||
if (!retain_gids)
|
||||
if (*requested_glyph_map)
|
||||
{
|
||||
hb_set_t new_gids(requested_glyph_map->values());
|
||||
if (new_gids.get_population() != requested_glyph_map->get_population())
|
||||
{
|
||||
DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (retain_gids)
|
||||
{
|
||||
DEBUG_MSG (SUBSET, nullptr,
|
||||
"HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if "
|
||||
"a custom glyph mapping has been provided.");
|
||||
return false;
|
||||
}
|
||||
|
||||
hb_codepoint_t max_glyph = 0;
|
||||
hb_set_t remaining;
|
||||
for (auto old_gid : all_gids_to_retain->iter ())
|
||||
{
|
||||
if (old_gid == 0) {
|
||||
reverse_glyph_map->set(0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
hb_codepoint_t* new_gid;
|
||||
if (!requested_glyph_map->has (old_gid, &new_gid))
|
||||
{
|
||||
remaining.add(old_gid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*new_gid > max_glyph)
|
||||
max_glyph = *new_gid;
|
||||
reverse_glyph_map->set (*new_gid, old_gid);
|
||||
}
|
||||
|
||||
// Anything that wasn't mapped by the requested mapping should
|
||||
// be placed after the requested mapping.
|
||||
for (auto old_gid : remaining)
|
||||
{
|
||||
reverse_glyph_map->set(++max_glyph, old_gid);
|
||||
}
|
||||
|
||||
*num_glyphs = max_glyph + 1;
|
||||
}
|
||||
else if (!retain_gids)
|
||||
{
|
||||
+ hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
|
||||
| hb_sink (reverse_glyph_map)
|
||||
|
@ -806,6 +854,8 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
|
|||
| hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
|
||||
| hb_sink (glyph_map)
|
||||
;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
|
@ -1008,12 +1058,16 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
|
|||
if (unlikely (in_error ()))
|
||||
return;
|
||||
|
||||
_create_old_gid_to_new_gid_map (face,
|
||||
input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
|
||||
&_glyphset,
|
||||
glyph_map,
|
||||
reverse_glyph_map,
|
||||
&_num_output_glyphs);
|
||||
if (!check_success(_create_old_gid_to_new_gid_map(
|
||||
face,
|
||||
input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
|
||||
&_glyphset,
|
||||
&input->glyph_map,
|
||||
glyph_map,
|
||||
reverse_glyph_map,
|
||||
&_num_output_glyphs))) {
|
||||
return;
|
||||
}
|
||||
|
||||
_create_glyph_map_gsub (
|
||||
&_glyphset_gsub,
|
||||
|
|
|
@ -154,6 +154,9 @@ hb_subset_input_glyph_set (hb_subset_input_t *input);
|
|||
HB_EXTERN hb_set_t *
|
||||
hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type);
|
||||
|
||||
HB_EXTERN hb_map_t*
|
||||
hb_subset_input_old_to_new_glyph_mapping (hb_subset_input_t *input);
|
||||
|
||||
HB_EXTERN hb_subset_flags_t
|
||||
hb_subset_input_get_flags (hb_subset_input_t *input);
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
2
test/subset/data/profiles/glyph_map_roboto.txt
Normal file
2
test/subset/data/profiles/glyph_map_roboto.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
--gid-map=37:1,39:2,69:3,74:4,77:5
|
||||
--layout-features+=c2sc
|
|
@ -0,0 +1,4 @@
|
|||
--gid-map=37:37,39:39,69:69,74:74,77:77,444:444,446:446,561:561,563:563
|
||||
--gids=444,446,561,563
|
||||
--layout-features+=c2sc
|
||||
--retain-gids
|
9
test/subset/data/tests/glyph_map.tests
Normal file
9
test/subset/data/tests/glyph_map.tests
Normal file
|
@ -0,0 +1,9 @@
|
|||
FONTS:
|
||||
Roboto-Regular.ttf
|
||||
|
||||
PROFILES:
|
||||
glyph_map_roboto.txt
|
||||
glyph_map_roboto_retain_gids.txt
|
||||
|
||||
SUBSETS:
|
||||
ACafi
|
|
@ -51,7 +51,9 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
|
|||
if unicodes != "":
|
||||
args.extend(["--unicodes=%s" % unicodes,])
|
||||
|
||||
args.extend(profile_flags)
|
||||
# --gid-map is unsupported in fonttools so don't send it. Tests using
|
||||
# it are crafted to work without fonttools knowing about the flag.
|
||||
args.extend([f for f in profile_flags if not f.startswith("--gid-map")])
|
||||
if not no_fonttools:
|
||||
check_call(args)
|
||||
|
||||
|
|
|
@ -742,6 +742,63 @@ parse_instance (const char *name,
|
|||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
parse_glyph_map (const char *name,
|
||||
const char *arg,
|
||||
gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
// Glyph map has the following format:
|
||||
// <entry 1>,<entry 2>,...,<entry n>
|
||||
// <entry> = <old gid>:<new gid>
|
||||
subset_main_t *subset_main = (subset_main_t *) data;
|
||||
hb_subset_input_t* input = subset_main->input;
|
||||
hb_set_t *glyphs = hb_subset_input_glyph_set(input);
|
||||
|
||||
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)
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||||
"Failed parsing glyph map at: '%s'", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p || p[0] != ':') // ranges
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||||
"Failed parsing glyph map at: '%s'", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
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 map at: '%s'", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
hb_set_add(glyphs, start_code);
|
||||
hb_map_set (hb_subset_input_old_to_new_glyph_mapping (input), start_code, end_code);
|
||||
|
||||
s = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <GOptionArgFunc line_parser, bool allow_comments=true>
|
||||
static gboolean
|
||||
parse_file_for (const char *name,
|
||||
|
@ -894,6 +951,7 @@ subset_main_t::add_options ()
|
|||
|
||||
GOptionEntry other_entries[] =
|
||||
{
|
||||
{"gid-map", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyph_map, "Specify a glyph mapping to use, any unmapped gids will be automatically assigned.", "List of pairs old_gid1:new_gid1,old_gid2:new_gid2,..."},
|
||||
{"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids. Use --name-IDs-=... to subtract 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 *"},
|
||||
|
|
Loading…
Add table
Reference in a new issue