added CFF1 desubr api test cases & bug fixes
diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh index ec8a7fa..0d267ac 100644 --- a/src/hb-cff1-interp-cs.hh +++ b/src/hb-cff1-interp-cs.hh
@@ -111,16 +111,9 @@ break; } env.set_width (has_width); - if (has_width) - { - OPSET::process_width (env, param); - } } } - static inline void process_width (CFF1CSInterpEnv &env, PARAM& param) - {} - static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param) { SUPER::flush_args (env, param);
diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index 40ae957..2f5e2f8 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh
@@ -93,7 +93,12 @@ { return calculate_offset_array_size (offSize, count); } inline static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize) - { return min_size + calculate_offset_array_size (offSize, count) + dataSize; } + { + if (count == 0) + return COUNT::static_size; + else + return min_size + calculate_offset_array_size (offSize, count) + dataSize; + } inline bool serialize (hb_serialize_context_t *c, const CFFIndex &src) { @@ -110,30 +115,39 @@ const ByteStrArray &byteArray) { TRACE_SERIALIZE (this); - /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (byteArray.len); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1)))) - return_trace (false); - - /* serialize indices */ - unsigned int offset = 1; - unsigned int i = 0; - for (; i < byteArray.len; i++) + if (byteArray.len == 0) { - set_offset_at (i, offset); - offset += byteArray[i].get_size (); + COUNT *dest = c->allocate_min<COUNT> (); + if (unlikely (dest == nullptr)) return_trace (false); + dest->set (0); } - set_offset_at (i, offset); - - /* serialize data */ - for (unsigned int i = 0; i < byteArray.len; i++) + else { - ByteStr *dest = c->start_embed<ByteStr> (); - if (unlikely (dest == nullptr || - !dest->serialize (c, byteArray[i]))) + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count.set (byteArray.len); + this->offSize.set (offSize_); + if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1)))) return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < byteArray.len; i++) + { + set_offset_at (i, offset); + offset += byteArray[i].get_size (); + } + set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < byteArray.len; i++) + { + ByteStr *dest = c->start_embed<ByteStr> (); + if (unlikely (dest == nullptr || + !dest->serialize (c, byteArray[i]))) + return_trace (false); + } } return_trace (true); }
diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 944d5e9..edee147 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh
@@ -378,8 +378,10 @@ inline void init (unsigned int subr_num_ = 0) { OpStr::init (); - flags = kDropFlag_None; subr_num = subr_num_; + drop_flag = false; + keep_flag = false; + skip_flag = false; } inline void fini (void) @@ -387,20 +389,19 @@ OpStr::fini (); } - inline bool for_keep (void) const { return (flags & kDropFlag_Keep) != 0; } - inline bool for_drop (void) const { return (flags & kDropFlag_Drop) != 0; } - inline void set_drop (void) { if (!for_keep ()) flags |= kDropFlag_Drop; } - inline void set_keep (void) { flags |= kDropFlag_Keep; } + inline bool for_drop (void) const { return drop_flag; } + inline void set_drop (void) { if (!for_keep ()) drop_flag = true; } + inline bool for_keep (void) const { return keep_flag; } + inline void set_keep (void) { keep_flag = true; } + inline bool for_skip (void) const { return skip_flag; } + inline void set_skip (void) { skip_flag = true; } - enum DropFlag - { - kDropFlag_None = 0, - kDropFlag_Drop = 1, - kDropFlag_Keep = 2 - }; - - unsigned int flags; unsigned int subr_num; + + protected: + bool drop_flag : 1; + bool keep_flag : 1; + bool skip_flag : 1; }; struct ParsedCStr : ParsedValues<ParsedCSOp> @@ -425,7 +426,7 @@ { unsigned int parsed_len = get_count (); if (likely (parsed_len > 0)) - values[parsed_len-1].set_drop (); + values[parsed_len-1].set_skip (); ParsedCSOp val; val.init (subr_num); @@ -629,8 +630,8 @@ * 4. re-encode all charstrings and subroutines with new subroutine numbers * * Phases #1 and #2 are done at the same time in collect_subrs (). - * Phase #3 requires walking charstrings/subroutines forward then backward (hence parsing), because - * we can't tell if a number belongs to a hint op until we see the first moveto. + * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required), + * because we can't tell if a number belongs to a hint op until we see the first moveto. * * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. @@ -686,7 +687,8 @@ drop_hints); bool seen_moveto = false; - if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto)) + bool ends_in_hint = false; + if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto, ends_in_hint)) parsed_charstrings[i].set_hint_removed (); } @@ -755,39 +757,39 @@ ParsedCStrs &subrs, unsigned int subr_num, const SubrSubsetParam ¶m, bool &seen_moveto) { - if (drop_hints_in_str (subrs[subr_num], param, seen_moveto)) - { - /* if the first op in the subr is a hint op, then all args/ops (especially including other subr calls) - * preceding this subr no and call op are hints */ - /* TODO CFF2 vsindex */ - for (unsigned int i = 0; i + 1 < pos; i++) - str.values[i].set_drop (); - return true; - } - else - return false; + bool ends_in_hint = false; + bool has_hint = drop_hints_in_str (subrs[subr_num], param, seen_moveto, ends_in_hint); + + /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto), + * then this entire subroutine must be a hint. drop its call. */ + if (ends_in_hint) + str.values[pos].set_drop (); + + return has_hint; } - /* returns true if it sees a hint op before moveto */ - inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, bool &seen_moveto) + /* returns true if it sees a hint op before the first moveto */ + inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, + bool &seen_moveto, bool &ends_in_hint) { bool seen_hint = false; - unsigned int next_check_pos = 0; for (unsigned int pos = 0; pos < str.values.len; pos++) { + bool has_hint = false; switch (str.values[pos].op) { case OpCode_callsubr: - seen_hint |= drop_hints_in_subr (str, pos, - *param.parsed_local_subrs, str.values[pos].subr_num, - param, seen_moveto); + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_local_subrs, str.values[pos].subr_num, + param, seen_moveto); + break; case OpCode_callgsubr: - seen_hint |= drop_hints_in_subr (str, pos, - *param.parsed_global_subrs, str.values[pos].subr_num, - param, seen_moveto); + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_global_subrs, str.values[pos].subr_num, + param, seen_moveto); break; case OpCode_rmoveto: @@ -809,19 +811,27 @@ case OpCode_vstemhm: case OpCode_hstem: case OpCode_vstem: - seen_hint = true; - for (unsigned int i = next_check_pos; i <= pos; i++) - { - /* TODO: CFF2 vsindex */ - str.values[i].set_drop (); - } - next_check_pos = pos + 1; + has_hint = true; + str.values[pos].set_drop (); + if ((pos + 1 >= str.values.len) /* CFF2 */ + || (str.values[pos + 1].op == OpCode_return)) + ends_in_hint = true; break; default: /* NONE */ break; } + if (has_hint) + { + for (int i = pos - 1; i >= 0; i--) + { + if (str.values[i].for_drop ()) + break; + str.values[i].set_drop (); + } + seen_hint |= has_hint; + } } return seen_hint; @@ -878,7 +888,7 @@ for (unsigned int i = 0; i < str.get_count(); i++) { const ParsedCSOp &opstr = str.values[i]; - if (!opstr.for_drop ()) + if (!opstr.for_drop () && !opstr.for_skip ()) { switch (opstr.op) {
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 414642f..216d095 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc
@@ -380,11 +380,6 @@ } } - static inline void process_width (CFF1CSInterpEnv &env, SubrSubsetParam& param) - { - - } - protected: static inline void process_call_subr (OpCode op, CSType type, CFF1CSInterpEnv &env, SubrSubsetParam& param, @@ -405,7 +400,7 @@ { static inline void set_parsed_prefix (const CFF1CSInterpEnv &env, ParsedCStr &charstring) { - if (env.processed_width) + if (env.has_width) charstring.set_prefix (env.width); } }; @@ -676,7 +671,7 @@ return false; /* no global/local subroutines */ - offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */ + offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (1, 0, 0); } else { @@ -704,14 +699,19 @@ for (unsigned int fd = 0; fd < orig_fdcount; fd++) { subset_localsubrs[fd].init (); + offsets.localSubrsInfos[fd].init (); if (fdmap.includes (fd)) { if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) return false; unsigned int dataSize = subset_localsubrs[fd].total_size (); - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize); + if (dataSize > 0) + { + offsets.localSubrsInfos[fd].offset = final_size; + offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); + offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize); + } } } } @@ -779,8 +779,11 @@ fontdicts_mod.push (fontdict_mod); final_size += privInfo.size; - if (!plan->desubroutinize) + if (!plan->desubroutinize && (offsets.localSubrsInfos[i].size > 0)) + { + offsets.localSubrsInfos[i].offset = final_size; final_size += offsets.localSubrsInfos[i].size; + } } } @@ -900,21 +903,12 @@ assert (plan.offsets.globalSubrsInfo.offset != 0); assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start); - if (plan.desubroutinize) + CFF1Subrs *dest = c.start_embed <CFF1Subrs> (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) { - CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size); - if (unlikely (dest == nullptr)) return false; - dest->count.set (0); - } - else - { - CFF1Subrs *dest = c.start_embed <CFF1Subrs> (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); - return false; - } + DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); + return false; } } @@ -1010,7 +1004,7 @@ assert (plan.offsets.privateDictInfo.offset == c.head - c.start); for (unsigned int i = 0; i < acc.privateDicts.len; i++) { - if (!plan.fdmap.excludes (i)) + if (plan.fdmap.includes (i)) { PrivateDict *pd = c.start_embed<PrivateDict> (); if (unlikely (pd == nullptr)) return false; @@ -1024,14 +1018,7 @@ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); return false; } - } - } - - if (!plan.desubroutinize) - { - for (unsigned int i = 0; i < acc.privateDicts.len; i++) - { - if (!plan.fdmap.excludes (i)) + if (plan.offsets.localSubrsInfos[i].size > 0) { CFF1Subrs *dest = c.start_embed <CFF1Subrs> (); if (unlikely (dest == nullptr)) return false;
diff --git a/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf b/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf index 08bc0e0..b4d1165 100644 --- a/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf +++ b/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf new file mode 100644 index 0000000..e054826 --- /dev/null +++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf new file mode 100644 index 0000000..00a112f --- /dev/null +++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf new file mode 100644 index 0000000..6fe9bf3 --- /dev/null +++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf index 113c1d7..7b88702 100644 --- a/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf +++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.abc.otf b/test/api/fonts/SourceSansPro-Regular.abc.otf index 1f1af4a..4db5c79 100644 --- a/test/api/fonts/SourceSansPro-Regular.abc.otf +++ b/test/api/fonts/SourceSansPro-Regular.abc.otf Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf b/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf index 30d9e34..3ad2bb4 100644 --- a/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf +++ b/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf new file mode 100644 index 0000000..69e244c --- /dev/null +++ b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf new file mode 100644 index 0000000..28edf13 --- /dev/null +++ b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.otf b/test/api/fonts/SourceSansPro-Regular.ac.otf index 40b938e..a22b157 100644 --- a/test/api/fonts/SourceSansPro-Regular.ac.otf +++ b/test/api/fonts/SourceSansPro-Regular.ac.otf Binary files differ
diff --git a/test/api/test-subset-cff1.c b/test/api/test-subset-cff1.c index 8f0c1b6..4aafb80 100644 --- a/test/api/test-subset-cff1.c +++ b/test/api/test-subset-cff1.c
@@ -92,6 +92,53 @@ } static void +test_subset_cff1_desubr (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_subset_input_t *input; + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_desubroutinize (input, true); + face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' ')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_cff1_desubr_strip_hints (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_subset_input_t *input; + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_drop_hints (input, true); + hb_subset_input_set_desubroutinize (input, true); + face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' ')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void test_subset_cff1_j (void) { hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); @@ -112,6 +159,76 @@ } static void +test_subset_cff1_j_strip_hints (void) +{ + hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); + hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nohints.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_41_3041_4c2e_subset; + hb_subset_input_t *input; + hb_set_add (codepoints, 0x41); + hb_set_add (codepoints, 0x4C2E); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_drop_hints (input, true); + face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' ')); + + hb_face_destroy (face_41_3041_4c2e_subset); + hb_face_destroy (face_41_3041_4c2e); + hb_face_destroy (face_41_4c2e); +} + +static void +test_subset_cff1_j_desubr (void) +{ + hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); + hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_41_3041_4c2e_subset; + hb_subset_input_t *input; + hb_set_add (codepoints, 0x41); + hb_set_add (codepoints, 0x4C2E); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_desubroutinize (input, true); + face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' ')); + + hb_face_destroy (face_41_3041_4c2e_subset); + hb_face_destroy (face_41_3041_4c2e); + hb_face_destroy (face_41_4c2e); +} + +static void +test_subset_cff1_j_desubr_strip_hints (void) +{ + hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); + hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_41_3041_4c2e_subset; + hb_subset_input_t *input; + hb_set_add (codepoints, 0x41); + hb_set_add (codepoints, 0x4C2E); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_drop_hints (input, true); + hb_subset_input_set_desubroutinize (input, true); + face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' ')); + + hb_face_destroy (face_41_3041_4c2e_subset); + hb_face_destroy (face_41_3041_4c2e); + hb_face_destroy (face_41_4c2e); +} + +static void test_subset_cff1_expert (void) { hb_face_t *face = hb_test_open_font_file ("fonts/cff1_expert.otf"); @@ -139,7 +256,12 @@ hb_test_add (test_subset_cff1_noop); hb_test_add (test_subset_cff1); hb_test_add (test_subset_cff1_strip_hints); + hb_test_add (test_subset_cff1_desubr); + hb_test_add (test_subset_cff1_desubr_strip_hints); hb_test_add (test_subset_cff1_j); + hb_test_add (test_subset_cff1_j_strip_hints); + hb_test_add (test_subset_cff1_j_desubr); + hb_test_add (test_subset_cff1_j_desubr_strip_hints); hb_test_add (test_subset_cff1_expert); return hb_test_run ();