From a88f3e8d379b7fbb0e4374f8ce1feb48ae681911 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 10:47:36 -0700 Subject: [PATCH 01/10] [subset-cff2] Don't serialize VarStore if pinned --- src/hb-subset-cff2.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index d2fcdceac..74e39d5ec 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -243,16 +243,17 @@ struct cff2_subr_subsetter_t : subr_subsetter_tcount; drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + pinned = (bool) plan->normalized_coords; desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE || - plan->normalized_coords; // For instancing we need this path + pinned; // For instancing we need this path if (desubroutinize) { @@ -311,8 +312,9 @@ struct cff2_subset_plan { unsigned int orig_fdcount = 0; unsigned int subset_fdcount = 1; - unsigned int subset_fdselect_size = 0; + unsigned int subset_fdselect_size = 0; unsigned int subset_fdselect_format = 0; + bool pinned = false; hb_vector_t subset_fdselect_ranges; hb_inc_bimap_t fdmap; @@ -424,7 +426,8 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, } /* variation store */ - if (acc.varStore != &Null (CFF2VariationStore)) + if (acc.varStore != &Null (CFF2VariationStore) && + !plan.pinned) { c->push (); CFF2VariationStore *dest = c->start_embed (); From 82d9940a938d20bc2a4864a10e53bff4e34762ad Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 10:51:55 -0700 Subject: [PATCH 02/10] [subset-cff2] Don't encode vsindex if pinned --- src/hb-subset-cff-common.hh | 8 ++++---- src/hb-subset-cff2.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 684ac5f65..2b7870c4e 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -738,7 +738,7 @@ struct subr_subsetter_t return true; } - bool encode_charstrings (str_buff_vec_t &buffArray) const + bool encode_charstrings (str_buff_vec_t &buffArray, bool encode_prefix = true) const { if (unlikely (!buffArray.resize_exact (plan->num_output_glyphs ()))) return false; @@ -754,7 +754,7 @@ struct subr_subsetter_t unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; - if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i]))) + if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i], encode_prefix))) return false; } return true; @@ -984,14 +984,14 @@ struct subr_subsetter_t } } - bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const + bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff, bool encode_prefix = true) const { str_encoder_t encoder (buff); encoder.reset (); bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, * re-insert it at the beginning of charstreing */ - if (str.has_prefix () && !hinting && str.is_hint_dropped ()) + if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) { encoder.encode_num (str.prefix_num ()); if (str.prefix_op () != OpCode_Invalid) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 74e39d5ec..5335ca997 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -272,7 +272,7 @@ struct cff2_subset_plan return false; /* encode charstrings, global subrs, local subrs with new subroutine numbers */ - if (!subr_subsetter.encode_charstrings (subset_charstrings)) + if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned)) return false; if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) From 220caa7e095b62fef55b03f32b65ddc564d8dc63 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 11:01:27 -0700 Subject: [PATCH 03/10] [subset-cff2] Only encode VarStore link if any varstore --- src/hb-subset-cff2.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 5335ca997..9bf28e87b 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -59,7 +59,10 @@ struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> switch (opstr.op) { case OpCode_vstore: - return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); + if (info.var_store_link) + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); + else + return_trace (true); default: return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); From bf4b34e87e5d7428b7b206a38ce6d7948657a9cb Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 11:16:51 -0700 Subject: [PATCH 04/10] [subset-cff2] Don't encode vsindex in Private dict --- src/hb-subset-cff-common.hh | 14 +++++++++----- src/hb-subset-cff2.cc | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 2b7870c4e..ea67dba1b 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -192,8 +192,8 @@ struct cff_font_dict_op_serializer_t : op_serializer_t struct cff_private_dict_op_serializer_t : op_serializer_t { - cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_) - : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} + cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_ = false) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_) {} bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, @@ -202,7 +202,10 @@ struct cff_private_dict_op_serializer_t : op_serializer_t TRACE_SERIALIZE (this); if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) - return true; + return_trace (true); + if (pinned && opstr.op == OpCode_vsindexdict) + return_trace (true); + if (opstr.op == OpCode_Subrs) { if (desubroutinize || !subrs_link) @@ -215,8 +218,9 @@ struct cff_private_dict_op_serializer_t : op_serializer_t } protected: - const bool desubroutinize; - const bool drop_hints; + const bool desubroutinize; + const bool drop_hints; + const bool pinned; }; struct flatten_param_t diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 9bf28e87b..23bab9d8b 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -361,7 +361,7 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, PrivateDict *pd = c->start_embed (); if (unlikely (!pd)) return false; c->push (); - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned); if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) { unsigned fd = plan.fdmap[i]; From c65eb5a82e4a9a0f3fe60b770741f3f4d3391bc2 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 15:15:57 -0700 Subject: [PATCH 05/10] [cff] Specialize cff_private_dict_op_serializer_t for CFF1/2 --- src/hb-subset-cff-common.hh | 33 --------------------------------- src/hb-subset-cff1.cc | 32 +++++++++++++++++++++++++++++++- src/hb-subset-cff2.cc | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index ea67dba1b..67197e89b 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -190,39 +190,6 @@ struct cff_font_dict_op_serializer_t : op_serializer_t } }; -struct cff_private_dict_op_serializer_t : op_serializer_t -{ - cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_ = false) - : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_) {} - - bool serialize (hb_serialize_context_t *c, - const op_str_t &opstr, - objidx_t subrs_link) const - { - TRACE_SERIALIZE (this); - - if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) - return_trace (true); - if (pinned && opstr.op == OpCode_vsindexdict) - return_trace (true); - - if (opstr.op == OpCode_Subrs) - { - if (desubroutinize || !subrs_link) - return_trace (true); - else - return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); - } - else - return_trace (copy_opstr (c, opstr)); - } - - protected: - const bool desubroutinize; - const bool drop_hints; - const bool pinned; -}; - struct flatten_param_t { str_buff_t &flatStr; diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 538f28f5a..11fc0b9af 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -335,6 +335,36 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t SUPER; }; +struct cff1_private_dict_op_serializer_t : op_serializer_t +{ + cff1_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} + + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + objidx_t subrs_link) const + { + TRACE_SERIALIZE (this); + + if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) + return_trace (true); + + if (opstr.op == OpCode_Subrs) + { + if (desubroutinize || !subrs_link) + return_trace (true); + else + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); + } + + return_trace (copy_opstr (c, opstr)); + } + + protected: + const bool desubroutinize; + const bool drop_hints; +}; + struct cff1_subr_subsetter_t : subr_subsetter_t { cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) @@ -721,7 +751,7 @@ static bool _serialize_cff1 (hb_serialize_context_t *c, PrivateDict *pd = c->start_embed (); if (unlikely (!pd)) return false; c->push (); - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) { diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 23bab9d8b..23fd89bfb 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -246,6 +246,40 @@ struct cff2_subr_subsetter_t : subr_subsetter_tstart_embed (); if (unlikely (!pd)) return false; c->push (); - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned); + cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned); if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) { unsigned fd = plan.fdmap[i]; From f10a4c9d6aebcc60b525a2342b0cdc6970ba32a3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 17:11:30 -0700 Subject: [PATCH 06/10] [cff] Rename encode_num to encode_num_cs --- src/hb-subset-cff-common.hh | 4 ++-- src/hb-subset-cff1.cc | 4 ++-- src/hb-subset-cff2.cc | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 67197e89b..ed9e0125a 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -81,7 +81,7 @@ struct str_encoder_t } } - void encode_num (const number_t& n) + void encode_num_cs (const number_t& n) { if (n.in_int_range ()) { @@ -964,7 +964,7 @@ struct subr_subsetter_t * re-insert it at the beginning of charstreing */ if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) { - encoder.encode_num (str.prefix_num ()); + encoder.encode_num_cs (str.prefix_num ()); if (str.prefix_op () != OpCode_Invalid) encoder.encode_op (str.prefix_op ()); } diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 11fc0b9af..1d7ed6444 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -234,7 +234,7 @@ struct cff1_cs_opset_flatten_t : cff1_cs_opset_t Date: Sun, 19 Feb 2023 17:48:02 -0700 Subject: [PATCH 07/10] [cff2-subset] Blend Private values when instancing --- src/hb-serialize.hh | 7 ++ src/hb-subset-cff-common.hh | 80 ++++++++++++++++++ src/hb-subset-cff2.cc | 159 ++++++++++++++++++++++++++++++++++-- 3 files changed, 239 insertions(+), 7 deletions(-) diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh index 0c2f64cc4..61ec0253a 100644 --- a/src/hb-serialize.hh +++ b/src/hb-serialize.hh @@ -629,6 +629,13 @@ struct hb_serialize_context_t template Type *embed (const Type &obj) { return embed (std::addressof (obj)); } + char *embed (const char *obj, unsigned size) + { + char *ret = this->allocate_size (size, false); + if (unlikely (!ret)) return nullptr; + hb_memcpy (ret, obj, size); + return ret; + } template auto _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index ed9e0125a..6351720e6 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -81,6 +81,7 @@ struct str_encoder_t } } + // Encode number for CharString void encode_num_cs (const number_t& n) { if (n.in_int_range ()) @@ -98,6 +99,85 @@ struct str_encoder_t } } + // Encode number for TopDict / Private + void encode_num_tp (const number_t& n) + { + if (n.in_int_range ()) + { + // TODO longint + encode_int (n.to_int ()); + } + else + { + // Sigh. BCD + // https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions + double v = n.to_real (); + encode_byte (OpCode_BCD); + + // Based on: + // https://github.com/fonttools/fonttools/blob/97ed3a61cde03e17b8be36f866192fbd56f1d1a7/Lib/fontTools/misc/psCharStrings.py#L265-L294 + + char buf[16]; + /* FontTools has the following comment: + * + * # Note: 14 decimal digits seems to be the limitation for CFF real numbers + * # in macOS. However, we use 8 here to match the implementation of AFDKO. + * + * We use 8 here to match FontTools X-). + */ + snprintf (buf, sizeof (buf), "%.8G", v); // XXX This is locale-sensitive; Ugh + char *s = buf; + if (s[0] == '0' && s[1] == '.') + s++; + else if (s[0] == '-' && s[1] == '0' && s[2] == '.') + { + s[1] = '-'; + s++; + } + hb_vector_t nibbles; + while (*s) + { + char c = s[0]; + s++; + + switch (c) + { + case 'E': + { + char c2 = *s; + if (c2 == '-') + { + s++; + nibbles.push (0x0C); // E- + continue; + } + if (c2 == '+') + s++; + nibbles.push (0x0B); // E + continue; + } + + case '.': case ',': // Comma for some European locales!!! + nibbles.push (0x0A); // . + continue; + + case '-': + nibbles.push (0x0E); // . + continue; + } + + nibbles.push (c - '0'); + } + nibbles.push (0x0F); + if (nibbles.length % 2) + nibbles.push (0x0F); + + unsigned count = nibbles.length; + for (unsigned i = 0; i < count; i += 2) + encode_byte ((nibbles[i] << 4) | nibbles[i+1]); + } + } + void encode_op (op_code_t op) { if (Is_OpCode_ESC (op)) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 5150d0109..6c5f69061 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -246,10 +246,143 @@ struct cff2_subr_subsetter_t : subr_subsetter_t normalized_coords) : + c (c), varStore (varStore), normalized_coords (normalized_coords) {} + + void init () {} + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (ivs); + scalars.resize_exact (region_count); + varStore->varStore.get_region_scalars (ivs, normalized_coords.arrayZ, normalized_coords.length, + &scalars[0], region_count); + seen_blend = true; + } + } + + double blend_deltas (hb_array_t deltas) const + { + double v = 0; + if (likely (scalars.length == deltas.length)) + { + unsigned count = scalars.length; + for (unsigned i = 0; i < count; i++) + v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); + } + return v; + } + + + hb_serialize_context_t *c = nullptr; + bool seen_blend = false; + unsigned ivs = 0; + unsigned region_count = 0; + hb_vector_t scalars; + const CFF2VariationStore *varStore = nullptr; + hb_array_t normalized_coords; +}; + +struct cff2_private_dict_blend_opset_t : dict_opset_t +{ + static void process_arg_blend (cff2_private_blend_encoder_param_t& param, + number_t &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + param.blend_deltas (blends)); + } + + static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param) + { + unsigned int n, k; + + param.process_blend (); + k = param.region_count; + n = env.argStack.pop_uint (); + /* copy the blend values into blend array of the default values */ + unsigned int start = env.argStack.get_count () - ((k+1) * n); + /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ + if (unlikely (start > env.argStack.get_count ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < n; i++) + { + const hb_array_t blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (param, env.argStack[start + i], blends, n, i); + } + + /* pop off blend values leaving default values now adorned with blend values */ + env.argStack.pop (k * n); + } + + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param) + { + switch (op) { + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ExpansionFactor: + case OpCode_LanguageGroup: + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + break; + case OpCode_vsindexdict: + env.process_vsindex (); + param.ivs = env.get_ivs (); + env.clear_args (); + return; + case OpCode_blenddict: + process_blend (env, param); + return; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + // Write args then op + + str_buff_t str; + str_encoder_t encoder (str); + + unsigned count = env.argStack.get_count (); + for (unsigned i = 0; i < count; i++) + encoder.encode_num_tp (env.argStack[i]); + + encoder.encode_op (op); + + auto bytes = str.as_bytes (); + param.c->embed (&bytes, bytes.length); + + env.clear_args (); + } +}; + struct cff2_private_dict_op_serializer_t : op_serializer_t { - cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_ = false) - : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_) {} + cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_, + const CFF::CFF2VariationStore* varStore_, + hb_array_t normalized_coords_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_), + varStore (varStore_), normalized_coords (normalized_coords_) {} bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, @@ -259,8 +392,6 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) return_trace (true); - if (pinned && opstr.op == OpCode_vsindexdict) - return_trace (true); if (opstr.op == OpCode_Subrs) { @@ -270,6 +401,15 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); } + if (pinned) + { + // Reinterpret opstr and process blends. + cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)}; + cff2_private_blend_encoder_param_t param (c, varStore, normalized_coords); + dict_interpreter_t interp (env); + return_trace (interp.interpret (param)); + } + return_trace (copy_opstr (c, opstr)); } @@ -277,6 +417,8 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t const bool desubroutinize; const bool drop_hints; const bool pinned; + const CFF::CFF2VariationStore* varStore; + hb_array_t normalized_coords; }; @@ -367,7 +509,8 @@ struct cff2_subset_plan static bool _serialize_cff2 (hb_serialize_context_t *c, cff2_subset_plan &plan, const OT::cff2::accelerator_subset_t &acc, - unsigned int num_glyphs) + unsigned int num_glyphs, + hb_array_t normalized_coords) { /* private dicts & local subrs */ hb_vector_t private_dict_infos; @@ -395,7 +538,8 @@ static bool _serialize_cff2 (hb_serialize_context_t *c, PrivateDict *pd = c->start_embed (); if (unlikely (!pd)) return false; c->push (); - cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned); + cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned, + acc.varStore, normalized_coords); if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) { unsigned fd = plan.fdmap[i]; @@ -503,7 +647,8 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, cff2_subset_plan cff2_plan; if (unlikely (!cff2_plan.create (acc, c->plan))) return false; - return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); + return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs (), + c->plan->normalized_coords.as_array ()); } bool From 21ff66cbd4445899f37c6aa6827a2d349292119b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 20:00:39 -0700 Subject: [PATCH 08/10] [subset-cff2] Round blended Private values when instancing Hopefully no one blends BlueScale... --- src/hb-subset-cff2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 6c5f69061..8ab462019 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -296,7 +296,7 @@ struct cff2_private_dict_blend_opset_t : dict_opset_t const hb_array_t blends, unsigned n, unsigned i) { - arg.set_real (arg.to_real () + param.blend_deltas (blends)); + arg.set_int (round (arg.to_real () + param.blend_deltas (blends))); } static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param) From 4a735b30c4e31489779a06722c66f8d820b9666d Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 19 Feb 2023 19:51:03 -0700 Subject: [PATCH 09/10] [cff2-subset] Update test --- .../api/fonts/AdobeVFPrototype.abc.static.otf | Bin 86112 -> 2576 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/api/fonts/AdobeVFPrototype.abc.static.otf b/test/api/fonts/AdobeVFPrototype.abc.static.otf index dde2c9b12d00effa3f846155eb5b036fa4d0e7e2..c5774cca102938661931e833d34d7c021a7e36a6 100644 GIT binary patch delta 772 zcmZvY&r4KM6vw~ky?Hv0Z=4z5APO@krUo*Ln1vu@_`8T=V$=e6{ldy0$vA~bm_)j8 z^`2MhAw+^Oh3!UY5iVNzAGFNH5G`{b1mU@KoUy^Txc76v_xrx*oLkzl+;}20KAwS% z6`1HB8XZlyF1;-PMhT#wUZT@%S$yXW_n8xa1KyjI{qgwQ91u7JtQ+1z)&NwLUIx<3 zg}8EQm6GbJ+e_coZNu#}U?H3Y&=u2blik6q{)i})Kp%_rVc)Y}le6sqp7sy?O~2%C z`Cs1G{Mp!2OSP@-pF+j@U1i%v*ADZK3A)2=QlO4`(%jap=J)Q#2(~f#?}iKnjjZ~* z*ZfKZ3vr`exCTA+OSl#x`XL-diarb1p~1`whj5wJgzFKd7s58uG%OrOkgf5m=~`NVg~o{0MqPzf+;-45T0QUdE{7M z;JxB#D|_0iT{rH2 literal 86112 zcmeHQ4|r6?wV&N&lK`oPYQP#w7Kj>bg-r+{0mTgh@)b0TM6mT=76^uiNmf`T{9i;V z5TNmsV2C1t+NTKC0Lq_$&%m?Qer+*m-&dY_R;sr0z4z)%TkXaM%sc1KIbruEH@kap zC_dlJe)pa^Gv}O{`Q7<*@67DIg+)b$B3T4PoN(VVd2;rl(^s?!;Ybp~{>H7I!WrNB zDC4K#ze@gm`=j^{ zludITCpB5ggr03e#BKd@A1LqXxV_`{_zD3bVJ=<^=_{^{E(lcNqRdvI_fDM47J%3KKpLNf8T%kjh~CSxU&!< zPS+J4m$YdTZ;l)^?P1}N!h2>BJsci#%uw=4KnZOA#9`xfhA21zX z!YRac{0(FgW4m@nKl@}j6a&Qjij^22-v|6ZfSM@%HZj&Vfc$oGnJtO@apDfBu~H^J zM8+ZJLW~Sg5H2xP`k@#JVi5V`pth%wKOTH=8AOFB7k;r2@LB|PmM9VP#RH-g{L>(A zu_yq$6=F7o&lHoP0xty4uN_N-4?=Gc6X84$^dA-vLd;Cy7J{w$AX5%VXNeJz8<)rt z5LlzGA@x1@nv9T0qCRDJfM|uI04}iVl*7Na2^eExnVX7ApHrV5VT{> zDQZjv%?V->AXfsV#u9iT_ZKWVp*$EeAI`HO7V|S%%fBCdUC}^1ePW5GAyLHqyCBE8 zARlTsQ^VK~(qqL)_$}6cBOo2ddxVBm8N>~Tz5<%U72k#{wxK_s2VI5drO6p^7UCYL zV*;)%o~J>bax{r1@UnA5a|m}B!ZI>8^bE4l?x@~s?aOBCJ4hUXB<=ma2{G~5WXFjn z+x50ZEd%x>v=x1k=U9+%uIPN8qs>3yoMhXO-2f=*u8(>Q?I6--sfu<>Ftjs9;!+WUcUB6 zEso$@i5qGh!Q#YC!J*5ZY)`0k_!B=~?>M(Q@wN>a>+c)9Yj3-KHG~ECZcp{x{oI>H z>1$Rv#=ri{g+EIRCbzT(M+Ar6^l5ui;iQV2W~Fag=6L&!*LNLHJ9~0MdwToGo2QIU z5AJTeKXuo>>w*JY zPP}}gA$>)SRtwv>ihWg^z^7?!ejLNlZ5x=-zPV=YcF9#$Z5o)kct=~=_Lu6m z)V^O=v*W;yr1niUeFoQ$xj(s2RdT-*<?02AU@s6shhacSl z#y1U2PHw4@QCb=h@Qu(q*dOaExTf1y{r;9!7pM#sBETNg6f3S(*VTpxM+0!$s1Nk7 zPh_22ai@Ky%0Di&UWb zmOYQ zhiU!d7@*j%y*T_DtsfhH{k2;E`p-i$wSIEgp5a>UJow6R*&jhah@<_HL%CW$x&Pt* zhWir(}*=R2NqDBjws1;SS1eF$0n8N6KI1_()x(GqMRw5)tZ8MB7&NBPO5(6au}1 zKo*TvP19&SglMlA{~8%_5l(I@lYWSQZp8=MdeRFJk8#Zza$ebge8yZw^#h;rs($-5qrG(fbO=o!*asU&@(KBDzR zn~91gWRGYu(Q2YiL|chEmr8lYk*SBm0|xO86yC5*>YpLXIM*+i;Xa~lS5J3Rz?n=b z@1gpKc3|fX@72+(8S=c|!}EvLBceaJ4|77LzSQ*kK72S|5^yD?VLX@=W z0ZPq}_Jn$89Nz3$aRDpJpnH+^t+U)INU;{x)%<8AfFrJNJ0n`K zCJ>?iL^N(yKVkw(Kp~(Ihyj6Onpa7od!K{4`8>7X_N%+!HipX_v#1w0M}vdE!D#c? zOnH8bsg&w^cG7%p*O;C|?lP%YZBp+olX~KxrCo3nbd1Dspopo94@6M_3BOPoi?eLQf97vQ%vdwOzIsrsn=>!FO$}Au=UnZQOG+EDSojX0Z(|0%@7_aCx<-AO`v`H!yn zM7uvvq5TeXFPA*1YL3_Tc|Qb)Gc^k5ctxW_pn$GVEA6Xbios@ju|HF?zV-6@R|h0D zYi}pptCDuuF52TdLLyNS-v3d(mh6$;Snat{0UWo7L%R6~iq`(48v2L+EU6}*`c?RT z5tirnLt@&0e$oF)jPhd)?E;J&BQTh;;8^@OZP9PVFK;luEz55mJs%Dm;8Jl!v`{9JEIDTce>4wtX=mucai z--m~TxE7ug5{^ZMSGmnVJN3uIC^xyvuFM>6^YE%e&mBlYaA9iDTb5J?a}>aGwqi)Yi|fC zde+G6mhvM>n<(5()JBxaG^$_t)&Pt%>Oa`Nc*6c6_7{4`{W0EqhaR6%g7vFqe$Egr zAOjTLY3>EePYQ%!oKb#^^sihd+Y8naGy0EF`WX@Qxx66O>9E{qxv?-#L^;3ZOJ)9U zNcyYx9Le;D#ABY($rn4yG9b_TrFVz7r?X@a-&Mo9D6e}Q+lzR5-X!(lL>FALhNCd1rb^YT=C zB}f#pfIw$^rFp8R$7Q{Y)ug_?w$l9)*JalRX_xCihjTjm`ft=;_4@kp7wzIv{isY2 zpLaM7q^#dc;~?LEvCI(i@HpwB`!hF(6HUtK#r8)wF9qT2&(9H^{dSC*;p@-u(L8*& zOM4$Dzn_qwffC^kp{U_fyFb+e=!~GTC=esGKX=#2q9;cVc=No(# zX;0T~AK5uOC%ipVy%o-%UZy=~^8btRxvKNoO4AM847=sv;8d8BVIXKLK+h-jM19q= z|Mn1VZ2T|nIjo-g;|tp1KE}hYf44u09Vgl~7vXl^`ngPR;+vAD9F_C|qHhyT-7eP^ zUnV;In3P-dx}9$vrjt=cHU`Gw5d;596m zwb;KwfQHC%e!itkkK;ML^((^j!|}0FPH%T+_x3u|Gl(~vUcP}nPVb0rJuudESf0yg zWUs$XO3rtMmmh=Sa8tB)Vd3Js@d8VyNw`tU;=fPX;8%i9rvk3OF&vLrGsn2as;A_? zUI=h|=J$g8_&JQ`H@^oj`HrTp@O@b`)lYqUW*hqT$r(dBIyerIWxrmIr%R)ik5-P; z`+8kfRhr&HfY<-^^_kZ(jn*rT)lE2mU=g+-UvO zXgxH0DJSHAeYN%-8Osj=WU*=xdsg%>P^W1Pu^NmZSd4HvOQ~0W8F*< z#E>Iv+A^V(7}7Hm)n9ERx$eWQ=74U|yEVQOKk5T6WxAX%W-5Lx$NNqy#Uh@klRbVP zntLYMiKwXr6aoqXg@8f;5vZqrw1MCl>kS|8uX|w_J{O1=cMsE^3!d72oi?5Bd~vw; z{jvu5t{TqGH)Q8%-x12r$5m^L z1J_FLD`3jw0aNX}xA1*i5S8Dq#qZm)AHHwf>BsNeayWk9mi#cHV(~Ox_^vSiRZIH2 z|GsS$UzdfCS?WJl2}YGu2q**;0tx|zfI>hapb$_9CwlI*aou|RWzoSVsX%X_|7VZ!Zy%@qWGB)%K0yxKD6LxrV#))#9 z&DM92I08x9`{7f3yd5IPi6-0iwnZ%i_CQEqu~I%|gZ~FM5BY6ktZe}K;fdasM1Fjt zKS+LfqVJT!C;B@m9G~ciO276*KZyMJM4v+b?w{yqi4rkiJRnMiUrZAfVliyBp9^$0 zgwGU{g-0v|&aWLygbzY*5EJ1%5B@Ituy_z+W&*cRECMSPqFgxP)f6KjH!hI_g)Y_f z=4dgGbjFPU8JEb1_%g5(#hgNz%>qb&f+z&-SaXUR6G3x=m;}g`K&i0=Uda6gOHL>c zhRlcaY=|v^Tu;{W?+0I3G;V=7pID-4NEDqS8%mcYazQ@SZl;E@AEd{Mk?>or{YF4K zjQ0rmt8E2fGExi&3^kWR^FaLnp=D{|^H)-;_s?$2Ce?b?g1M+_=nVRHy(3f}P5Yh1^oI+g3 z-#`}Y3f{5f|6AfZeKH)H#&om{F8OG9{hjES`-`wIO@V!(Ma1LX4)Sw}?*n{1@TJUy zy#(OPiSGw|9Pkejk86m1fM3e-!1o3IG2)X!9_?=-{u{ve2mU9-UqR^s`AWL!l&j?i z|Nn_K)B8waLanr2yEz2Blzw!F6<7sD4PH0bWA9?eX(dpqi@!|^#VsNWT_C{~` z{|E8^Vb7#(+wx@H=Ja=bQ}-97wfAkek8ICs&-}x6!GSF&UOv%~zM{r)-|Rd6o;2t9 z4}$}OSGRolS^A+E5(m*!B))NBI9{aSgTsyVtLpkAkl^&?k)hF_v0I139lb{HO5*be9G266h_ZE~uXpNeq*8Kv8<&Nr zhvNv=AwGJm4xK`Sn%%GN%vwTQ(HD7+1qtVh&gVJW`~%KOwhh@m5c+)T7osiayf~MW zxS%4n&HqK-Isf^*gcF~qjz8{w=j5TiCx0=xcH6p3GK3teN)2AV_D3y_;9H3sY8=6f zH!cKsx80w*@1;Q5E9s>#JwM)Ix<_AL*H&J8pth#=gW8(9!#g&ug`Gd^tKsy+S~yj0 zhGT05`!-;#7PfH}`>Hm9Pt(}^IEJCyHZY-mbIscAlEbY;iHmo%m2H2iZcFX^bu~K< z>_}?gRMTg0{h0fc`&1?OJ5l~@Rn?-61N*kj3fe(wK5;)s(Sd*4PYF0A|s zFHTFJ_%7+=gFQI+bh+P{DbHgtl~UHvZp$X{4%xiZZvRcITho@wTAhtUD>P`DEQi$` zP#4})>ee!7v3xyc4C9=U1R};;MG)rT-tBoF&nOGUCiQAf>NT6xOZttu^0`gwRhram zFsXOeKyM#iI1l|_gWqR!srCm~|C_;e;C`R-d)veE{*UXO{{5aD&eTo%9tzkrJN*7p zd??e+BkCi1hUirv$#@S@w%eaNmv6rxZja}uSZ?y9iMQgxJkM(}|Bt5le`7p2UHtzp z9?bK+9`kz2^I-b$i^cPNBHQPAKIVBIk9j@ic}))I`9S8U6cyZt2lHkrKOesKP^ zHy@>cA2x|~A*(6mUfuj#d>_cp*tlYtz_5WI_A$iq9tGIP08gVb9QHAE%D_Gb`!f^{ z`xxvGN!=@VB3k`>iC;>Kq+JRgAz+Gu z9)Ew}HQoAGZ?Es+S|4_1c(;xI<#6C!wBEHEH(XQ_pty>w{@2zki5B2M7JTB_jzj%C%cAbmIOZ_?^kDK~+v{=ez=DNQ?-(%qR zv_g0Pmi(AppK?M*!rGZGW2}!dVRE#e0`;}_NW8v1o+5hzdfx|sCxwEA=Zv}jbDc>& zPB&KCObTwIpSr`>JM{0XH?nVpXLY;WPm=+Ph$r)UJdO1EJ&Gx$&%9oLHSV_nSG-%| z{)3}2;!pSY(n|Daeoly9LrU^mzKkvkK{#iwu{V)T=kCciN<0${);?Z;DC1fJwc>CiPlP>Sg}X94^Hs^=eJ( zHJj8+`ovuM+$QxZP3kq6)H`cZ&w17yE_a#Ki?$y!l6&YWqZk#g5Kssx1QY@a0fm4< zKp~(IPzWdl6aoqXg@8gpA)pXY2t-1l|6`FPRJ=k!A)pXY2q**;0tx|zfI>hapb$_9 zCy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4yN Date: Sun, 19 Feb 2023 20:30:38 -0700 Subject: [PATCH 10/10] [subset-cff] Make BCD writing locale-independent --- src/hb-common.cc | 26 -------------------------- src/hb-subset-cff-common.hh | 10 ++++++++-- src/hb.hh | 31 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/hb-common.cc b/src/hb-common.cc index e9f9cfeb5..c9a40295a 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -29,32 +29,6 @@ #include "hb.hh" #include "hb-machinery.hh" -#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE)) -#define HB_NO_SETLOCALE 1 -#endif - -#ifndef HB_NO_SETLOCALE - -#include -#ifdef HAVE_XLOCALE_H -#include // Needed on BSD/OS X for uselocale -#endif - -#ifdef WIN32 -#define hb_locale_t _locale_t -#else -#define hb_locale_t locale_t -#endif -#define hb_setlocale setlocale -#define hb_uselocale uselocale - -#else - -#define hb_locale_t void * -#define hb_setlocale(Category, Locale) "C" -#define hb_uselocale(Locale) ((hb_locale_t) 0) - -#endif /** * SECTION:hb-common diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 6351720e6..ff50b0e51 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -125,7 +125,13 @@ struct str_encoder_t * * We use 8 here to match FontTools X-). */ - snprintf (buf, sizeof (buf), "%.8G", v); // XXX This is locale-sensitive; Ugh + + hb_locale_t clocale HB_UNUSED; + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (clocale = newlocale (LC_ALL_MASK, "C", NULL)); + snprintf (buf, sizeof (buf), "%.8G", v); + (void) hb_uselocale (((void) freelocale (clocale), oldlocale)); + char *s = buf; if (s[0] == '0' && s[1] == '.') s++; @@ -157,7 +163,7 @@ struct str_encoder_t continue; } - case '.': case ',': // Comma for some European locales!!! + case '.': case ',': // Comma for some European locales in case no uselocale available. nibbles.push (0x0A); // . continue; diff --git a/src/hb.hh b/src/hb.hh index aac43a295..857571b42 100644 --- a/src/hb.hh +++ b/src/hb.hh @@ -463,6 +463,37 @@ static int HB_UNUSED _hb_errno = 0; #endif #endif + +// Locale business + +#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE)) +#define HB_NO_SETLOCALE 1 +#endif + +#ifndef HB_NO_SETLOCALE + +#include +#ifdef HAVE_XLOCALE_H +#include // Needed on BSD/OS X for uselocale +#endif + +#ifdef WIN32 +#define hb_locale_t _locale_t +#else +#define hb_locale_t locale_t +#endif +#define hb_setlocale setlocale +#define hb_uselocale uselocale + +#else + +#define hb_locale_t void * +#define hb_setlocale(Category, Locale) "C" +#define hb_uselocale(Locale) ((hb_locale_t) 0) + +#endif + + /* Lets assert int types. Saves trouble down the road. */ static_assert ((sizeof (hb_codepoint_t) == 4), ""); static_assert ((sizeof (hb_position_t) == 4), "");