| 1 | /* | 
| 2 |  * Copyright © 2018 Adobe Inc. | 
| 3 |  * | 
| 4 |  *  This is part of HarfBuzz, a text shaping library. | 
| 5 |  * | 
| 6 |  * Permission is hereby granted, without written agreement and without | 
| 7 |  * license or royalty fees, to use, copy, modify, and distribute this | 
| 8 |  * software and its documentation for any purpose, provided that the | 
| 9 |  * above copyright notice and the following two paragraphs appear in | 
| 10 |  * all copies of this software. | 
| 11 |  * | 
| 12 |  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 
| 13 |  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 
| 14 |  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 
| 15 |  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | 
| 16 |  * DAMAGE. | 
| 17 |  * | 
| 18 |  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 
| 19 |  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 
| 20 |  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS | 
| 21 |  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 
| 22 |  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 
| 23 |  * | 
| 24 |  * Adobe Author(s): Michiharu Ariza | 
| 25 |  */ | 
| 26 |  | 
| 27 | #include "hb.hh" | 
| 28 |  | 
| 29 | #ifndef HB_NO_SUBSET_CFF | 
| 30 |  | 
| 31 | #include "hb-open-type.hh" | 
| 32 | #include "hb-ot-cff1-table.hh" | 
| 33 | #include "hb-set.h" | 
| 34 | #include "hb-bimap.hh" | 
| 35 | #include "hb-subset-cff1.hh" | 
| 36 | #include "hb-subset-plan.hh" | 
| 37 | #include "hb-subset-cff-common.hh" | 
| 38 | #include "hb-cff1-interp-cs.hh" | 
| 39 |  | 
| 40 | using namespace CFF; | 
| 41 |  | 
| 42 | struct remap_sid_t : hb_inc_bimap_t | 
| 43 | { | 
| 44 |   unsigned int add (unsigned int sid) | 
| 45 |   { | 
| 46 |     if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) | 
| 47 |       return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid))); | 
| 48 |     else | 
| 49 |       return sid; | 
| 50 |   } | 
| 51 |  | 
| 52 |   unsigned int operator[] (unsigned int sid) const | 
| 53 |   { | 
| 54 |     if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) | 
| 55 |       return sid; | 
| 56 |     else | 
| 57 |       return offset_sid (get (unoffset_sid (sid))); | 
| 58 |   } | 
| 59 |  | 
| 60 |   static const unsigned int num_std_strings = 391; | 
| 61 |  | 
| 62 |   static bool is_std_std (unsigned int sid) { return sid < num_std_strings; } | 
| 63 |   static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; } | 
| 64 |   static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; } | 
| 65 | }; | 
| 66 |  | 
| 67 | struct cff1_sub_table_info_t : cff_sub_table_info_t | 
| 68 | { | 
| 69 |   cff1_sub_table_info_t () | 
| 70 |     : cff_sub_table_info_t (), | 
| 71 |       encoding_link (0), | 
| 72 |       charset_link (0) | 
| 73 |    { | 
| 74 |     privateDictInfo.init (); | 
| 75 |   } | 
| 76 |  | 
| 77 |   objidx_t	encoding_link; | 
| 78 |   objidx_t	charset_link; | 
| 79 |   table_info_t	privateDictInfo; | 
| 80 | }; | 
| 81 |  | 
| 82 | /* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */ | 
| 83 | struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t | 
| 84 | { | 
| 85 |   void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t)) | 
| 86 |   { | 
| 87 |     SUPER::init (); | 
| 88 |     base = base_; | 
| 89 |   } | 
| 90 |  | 
| 91 |   void fini () { SUPER::fini (); } | 
| 92 |  | 
| 93 |   unsigned get_count () const { return base->get_count () + SUPER::get_count (); } | 
| 94 |   const cff1_top_dict_val_t &get_value (unsigned int i) const | 
| 95 |   { | 
| 96 |     if (i < base->get_count ()) | 
| 97 |       return (*base)[i]; | 
| 98 |     else | 
| 99 |       return SUPER::values[i - base->get_count ()]; | 
| 100 |   } | 
| 101 |   const cff1_top_dict_val_t &operator [] (unsigned int i) const { return get_value (i); } | 
| 102 |  | 
| 103 |   void reassignSIDs (const remap_sid_t& sidmap) | 
| 104 |   { | 
| 105 |     for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) | 
| 106 |       nameSIDs[i] = sidmap[base->nameSIDs[i]]; | 
| 107 |   } | 
| 108 |  | 
| 109 |   protected: | 
| 110 |   typedef cff1_top_dict_values_t SUPER; | 
| 111 |   const cff1_top_dict_values_t *base; | 
| 112 | }; | 
| 113 |  | 
| 114 | struct top_dict_modifiers_t | 
| 115 | { | 
| 116 |   top_dict_modifiers_t (const cff1_sub_table_info_t &info_, | 
| 117 | 			const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) | 
| 118 |     : info (info_), | 
| 119 |       nameSIDs (nameSIDs_) | 
| 120 |   {} | 
| 121 |  | 
| 122 |   const cff1_sub_table_info_t &info; | 
| 123 |   const unsigned int	(&nameSIDs)[name_dict_values_t::ValCount]; | 
| 124 | }; | 
| 125 |  | 
| 126 | struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dict_val_t> | 
| 127 | { | 
| 128 |   bool serialize (hb_serialize_context_t *c, | 
| 129 | 		  const cff1_top_dict_val_t &opstr, | 
| 130 | 		  const top_dict_modifiers_t &mod) const | 
| 131 |   { | 
| 132 |     TRACE_SERIALIZE (this); | 
| 133 |  | 
| 134 |     op_code_t op = opstr.op; | 
| 135 |     switch (op) | 
| 136 |     { | 
| 137 |       case OpCode_charset: | 
| 138 | 	if (mod.info.charset_link) | 
| 139 | 	  return_trace (FontDict::serialize_link4_op(c, op, mod.info.charset_link, whence_t::Absolute)); | 
| 140 | 	else | 
| 141 | 	  goto fall_back; | 
| 142 |  | 
| 143 |       case OpCode_Encoding: | 
| 144 | 	if (mod.info.encoding_link) | 
| 145 | 	  return_trace (FontDict::serialize_link4_op(c, op, mod.info.encoding_link, whence_t::Absolute)); | 
| 146 | 	else | 
| 147 | 	  goto fall_back; | 
| 148 |  | 
| 149 |       case OpCode_Private: | 
| 150 | 	return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) && | 
| 151 | 		      Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute)); | 
| 152 |  | 
| 153 |       case OpCode_version: | 
| 154 |       case OpCode_Notice: | 
| 155 |       case OpCode_Copyright: | 
| 156 |       case OpCode_FullName: | 
| 157 |       case OpCode_FamilyName: | 
| 158 |       case OpCode_Weight: | 
| 159 |       case OpCode_PostScript: | 
| 160 |       case OpCode_BaseFontName: | 
| 161 |       case OpCode_FontName: | 
| 162 | 	return_trace (FontDict::serialize_int2_op (c, op, mod.nameSIDs[name_dict_values_t::name_op_to_index (op)])); | 
| 163 |  | 
| 164 |       case OpCode_ROS: | 
| 165 | 	{ | 
| 166 | 	  /* for registry & ordering, reassigned SIDs are serialized | 
| 167 | 	   * for supplement, the original byte string is copied along with the op code */ | 
| 168 | 	  op_str_t supp_op; | 
| 169 | 	  supp_op.op = op; | 
| 170 | 	  if ( unlikely (!(opstr.str.length >= opstr.last_arg_offset + 3))) | 
| 171 | 	    return_trace (false); | 
| 172 | 	  supp_op.str = byte_str_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset); | 
| 173 | 	  return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) && | 
| 174 | 			UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) && | 
| 175 | 			copy_opstr (c, supp_op)); | 
| 176 | 	} | 
| 177 |       fall_back: | 
| 178 |       default: | 
| 179 | 	return_trace (cff_top_dict_op_serializer_t<cff1_top_dict_val_t>::serialize (c, opstr, mod.info)); | 
| 180 |     } | 
| 181 |     return_trace (true); | 
| 182 |   } | 
| 183 |  | 
| 184 | }; | 
| 185 |  | 
| 186 | struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t | 
| 187 | { | 
| 188 |   bool serialize (hb_serialize_context_t *c, | 
| 189 | 		  const op_str_t &opstr, | 
| 190 | 		  const cff1_font_dict_values_mod_t &mod) const | 
| 191 |   { | 
| 192 |     TRACE_SERIALIZE (this); | 
| 193 |  | 
| 194 |     if (opstr.op == OpCode_FontName) | 
| 195 |       return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName)); | 
| 196 |     else | 
| 197 |       return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo)); | 
| 198 |   } | 
| 199 |  | 
| 200 |   private: | 
| 201 |   typedef cff_font_dict_op_serializer_t SUPER; | 
| 202 | }; | 
| 203 |  | 
| 204 | struct cff1_cs_opset_flatten_t : cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> | 
| 205 | { | 
| 206 |   static void flush_args_and_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) | 
| 207 |   { | 
| 208 |     if (env.arg_start > 0) | 
| 209 |       flush_width (env, param); | 
| 210 |  | 
| 211 |     switch (op) | 
| 212 |     { | 
| 213 |       case OpCode_hstem: | 
| 214 |       case OpCode_hstemhm: | 
| 215 |       case OpCode_vstem: | 
| 216 |       case OpCode_vstemhm: | 
| 217 |       case OpCode_hintmask: | 
| 218 |       case OpCode_cntrmask: | 
| 219 |       case OpCode_dotsection: | 
| 220 | 	if (param.drop_hints) | 
| 221 | 	{ | 
| 222 | 	  env.clear_args (); | 
| 223 | 	  return; | 
| 224 | 	} | 
| 225 | 	HB_FALLTHROUGH; | 
| 226 |  | 
| 227 |       default: | 
| 228 | 	SUPER::flush_args_and_op (op, env, param); | 
| 229 | 	break; | 
| 230 |     } | 
| 231 |   } | 
| 232 |   static void flush_args (cff1_cs_interp_env_t &env, flatten_param_t& param) | 
| 233 |   { | 
| 234 |     str_encoder_t  encoder (param.flatStr); | 
| 235 |     for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++) | 
| 236 |       encoder.encode_num (env.eval_arg (i)); | 
| 237 |     SUPER::flush_args (env, param); | 
| 238 |   } | 
| 239 |  | 
| 240 |   static void flush_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) | 
| 241 |   { | 
| 242 |     str_encoder_t  encoder (param.flatStr); | 
| 243 |     encoder.encode_op (op); | 
| 244 |   } | 
| 245 |  | 
| 246 |   static void flush_width (cff1_cs_interp_env_t &env, flatten_param_t& param) | 
| 247 |   { | 
| 248 |     assert (env.has_width); | 
| 249 |     str_encoder_t  encoder (param.flatStr); | 
| 250 |     encoder.encode_num (env.width); | 
| 251 |   } | 
| 252 |  | 
| 253 |   static void flush_hintmask (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) | 
| 254 |   { | 
| 255 |     SUPER::flush_hintmask (op, env, param); | 
| 256 |     if (!param.drop_hints) | 
| 257 |     { | 
| 258 |       str_encoder_t  encoder (param.flatStr); | 
| 259 |       for (unsigned int i = 0; i < env.hintmask_size; i++) | 
| 260 | 	encoder.encode_byte (env.str_ref[i]); | 
| 261 |     } | 
| 262 |   } | 
| 263 |  | 
| 264 |   private: | 
| 265 |   typedef cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> SUPER; | 
| 266 | }; | 
| 267 |  | 
| 268 | struct range_list_t : hb_vector_t<code_pair_t> | 
| 269 | { | 
| 270 |   /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ | 
| 271 |   bool complete (unsigned int last_glyph) | 
| 272 |   { | 
| 273 |     bool  two_byte = false; | 
| 274 |     for (unsigned int i = (*this).length; i > 0; i--) | 
| 275 |     { | 
| 276 |       code_pair_t &pair = (*this)[i - 1]; | 
| 277 |       unsigned int  nLeft = last_glyph - pair.glyph - 1; | 
| 278 |       if (nLeft >= 0x100) | 
| 279 | 	two_byte = true; | 
| 280 |       last_glyph = pair.glyph; | 
| 281 |       pair.glyph = nLeft; | 
| 282 |     } | 
| 283 |     return two_byte; | 
| 284 |   } | 
| 285 | }; | 
| 286 |  | 
| 287 | struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> | 
| 288 | { | 
| 289 |   static void process_op (op_code_t op, cff1_cs_interp_env_t &env, subr_subset_param_t& param) | 
| 290 |   { | 
| 291 |     switch (op) { | 
| 292 |  | 
| 293 |       case OpCode_return: | 
| 294 | 	param.current_parsed_str->add_op (op, env.str_ref); | 
| 295 | 	param.current_parsed_str->set_parsed (); | 
| 296 | 	env.return_from_subr (); | 
| 297 | 	param.set_current_str (env, false); | 
| 298 | 	break; | 
| 299 |  | 
| 300 |       case OpCode_endchar: | 
| 301 | 	param.current_parsed_str->add_op (op, env.str_ref); | 
| 302 | 	param.current_parsed_str->set_parsed (); | 
| 303 | 	SUPER::process_op (op, env, param); | 
| 304 | 	break; | 
| 305 |  | 
| 306 |       case OpCode_callsubr: | 
| 307 | 	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); | 
| 308 | 	break; | 
| 309 |  | 
| 310 |       case OpCode_callgsubr: | 
| 311 | 	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); | 
| 312 | 	break; | 
| 313 |  | 
| 314 |       default: | 
| 315 | 	SUPER::process_op (op, env, param); | 
| 316 | 	param.current_parsed_str->add_op (op, env.str_ref); | 
| 317 | 	break; | 
| 318 |     } | 
| 319 |   } | 
| 320 |  | 
| 321 |   protected: | 
| 322 |   static void process_call_subr (op_code_t op, cs_type_t type, | 
| 323 | 				 cff1_cs_interp_env_t &env, subr_subset_param_t& param, | 
| 324 | 				 cff1_biased_subrs_t& subrs, hb_set_t *closure) | 
| 325 |   { | 
| 326 |     byte_str_ref_t    str_ref = env.str_ref; | 
| 327 |     env.call_subr (subrs, type); | 
| 328 |     param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); | 
| 329 |     closure->add (env.context.subr_num); | 
| 330 |     param.set_current_str (env, true); | 
| 331 |   } | 
| 332 |  | 
| 333 |   private: | 
| 334 |   typedef cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> SUPER; | 
| 335 | }; | 
| 336 |  | 
| 337 | struct cff1_subr_subsetter_t : subr_subsetter_t<cff1_subr_subsetter_t, CFF1Subrs, const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, OpCode_endchar> | 
| 338 | { | 
| 339 |   cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) | 
| 340 |     : subr_subsetter_t (acc_, plan_) {} | 
| 341 |  | 
| 342 |   static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) | 
| 343 |   { | 
| 344 |     /* insert width at the beginning of the charstring as necessary */ | 
| 345 |     if (env.has_width) | 
| 346 |       charstring.set_prefix (env.width); | 
| 347 |  | 
| 348 |     /* subroutines/charstring left on the call stack are legally left unmarked | 
| 349 |      * unmarked when a subroutine terminates with endchar. mark them. | 
| 350 |      */ | 
| 351 |     param.current_parsed_str->set_parsed (); | 
| 352 |     for (unsigned int i = 0; i < env.callStack.get_count (); i++) | 
| 353 |     { | 
| 354 |       parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); | 
| 355 |       if (likely (parsed_str)) | 
| 356 | 	parsed_str->set_parsed (); | 
| 357 |       else | 
| 358 | 	env.set_error (); | 
| 359 |     } | 
| 360 |   } | 
| 361 | }; | 
| 362 |  | 
| 363 | struct cff_subset_plan { | 
| 364 |   cff_subset_plan () | 
| 365 |     : info (), | 
| 366 |       orig_fdcount (0), | 
| 367 |       subset_fdcount (1), | 
| 368 |       subset_fdselect_format (0), | 
| 369 |       drop_hints (false), | 
| 370 |       desubroutinize(false) | 
| 371 |   { | 
| 372 |     topdict_mod.init (); | 
| 373 |     subset_fdselect_ranges.init (); | 
| 374 |     fdmap.init (); | 
| 375 |     subset_charstrings.init (); | 
| 376 |     subset_globalsubrs.init (); | 
| 377 |     subset_localsubrs.init (); | 
| 378 |     fontdicts_mod.init (); | 
| 379 |     subset_enc_code_ranges.init (); | 
| 380 |     subset_enc_supp_codes.init (); | 
| 381 |     subset_charset_ranges.init (); | 
| 382 |     sidmap.init (); | 
| 383 |     for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) | 
| 384 |       topDictModSIDs[i] = CFF_UNDEF_SID; | 
| 385 |   } | 
| 386 |  | 
| 387 |   ~cff_subset_plan () | 
| 388 |   { | 
| 389 |     topdict_mod.fini (); | 
| 390 |     subset_fdselect_ranges.fini (); | 
| 391 |     fdmap.fini (); | 
| 392 |     subset_charstrings.fini_deep (); | 
| 393 |     subset_globalsubrs.fini_deep (); | 
| 394 |     subset_localsubrs.fini_deep (); | 
| 395 |     fontdicts_mod.fini (); | 
| 396 |     subset_enc_code_ranges.fini (); | 
| 397 |     subset_enc_supp_codes.fini (); | 
| 398 |     subset_charset_ranges.fini (); | 
| 399 |     sidmap.fini (); | 
| 400 |   } | 
| 401 |  | 
| 402 |   void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) | 
| 403 |   { | 
| 404 |     const Encoding *encoding = acc.encoding; | 
| 405 |     unsigned int  size0, size1, supp_size; | 
| 406 |     hb_codepoint_t  code, last_code = CFF_UNDEF_CODE; | 
| 407 |     hb_vector_t<hb_codepoint_t> supp_codes; | 
| 408 |  | 
| 409 |     subset_enc_code_ranges.resize (0); | 
| 410 |     supp_size = 0; | 
| 411 |     supp_codes.init (); | 
| 412 |  | 
| 413 |     subset_enc_num_codes = plan->num_output_glyphs () - 1; | 
| 414 |     unsigned int glyph; | 
| 415 |     for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) | 
| 416 |     { | 
| 417 |       hb_codepoint_t  old_glyph; | 
| 418 |       if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) | 
| 419 |       { | 
| 420 | 	/* Retain the code for the old missing glyph ID */ | 
| 421 | 	old_glyph = glyph; | 
| 422 |       } | 
| 423 |       code = acc.glyph_to_code (old_glyph); | 
| 424 |       if (code == CFF_UNDEF_CODE) | 
| 425 |       { | 
| 426 | 	subset_enc_num_codes = glyph - 1; | 
| 427 | 	break; | 
| 428 |       } | 
| 429 |  | 
| 430 |       if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1)) | 
| 431 |       { | 
| 432 | 	code_pair_t pair = { code, glyph }; | 
| 433 | 	subset_enc_code_ranges.push (pair); | 
| 434 |       } | 
| 435 |       last_code = code; | 
| 436 |  | 
| 437 |       if (encoding != &Null (Encoding)) | 
| 438 |       { | 
| 439 | 	hb_codepoint_t  sid = acc.glyph_to_sid (old_glyph); | 
| 440 | 	encoding->get_supplement_codes (sid, supp_codes); | 
| 441 | 	for (unsigned int i = 0; i < supp_codes.length; i++) | 
| 442 | 	{ | 
| 443 | 	  code_pair_t pair = { supp_codes[i], sid }; | 
| 444 | 	  subset_enc_supp_codes.push (pair); | 
| 445 | 	} | 
| 446 | 	supp_size += SuppEncoding::static_size * supp_codes.length; | 
| 447 |       } | 
| 448 |     } | 
| 449 |     supp_codes.fini (); | 
| 450 |  | 
| 451 |     subset_enc_code_ranges.complete (glyph); | 
| 452 |  | 
| 453 |     assert (subset_enc_num_codes <= 0xFF); | 
| 454 |     size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes; | 
| 455 |     size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.length; | 
| 456 |  | 
| 457 |     if (size0 < size1) | 
| 458 |       subset_enc_format = 0; | 
| 459 |     else | 
| 460 |       subset_enc_format = 1; | 
| 461 |   } | 
| 462 |  | 
| 463 |   void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) | 
| 464 |   { | 
| 465 |     unsigned int  size0, size_ranges; | 
| 466 |     hb_codepoint_t  sid, last_sid = CFF_UNDEF_CODE; | 
| 467 |  | 
| 468 |     subset_charset_ranges.resize (0); | 
| 469 |     unsigned int glyph; | 
| 470 |     for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) | 
| 471 |     { | 
| 472 |       hb_codepoint_t  old_glyph; | 
| 473 |       if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) | 
| 474 |       { | 
| 475 | 	/* Retain the SID for the old missing glyph ID */ | 
| 476 | 	old_glyph = glyph; | 
| 477 |       } | 
| 478 |       sid = acc.glyph_to_sid (old_glyph); | 
| 479 |  | 
| 480 |       if (!acc.is_CID ()) | 
| 481 | 	sid = sidmap.add (sid); | 
| 482 |  | 
| 483 |       if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1)) | 
| 484 |       { | 
| 485 | 	code_pair_t pair = { sid, glyph }; | 
| 486 | 	subset_charset_ranges.push (pair); | 
| 487 |       } | 
| 488 |       last_sid = sid; | 
| 489 |     } | 
| 490 |  | 
| 491 |     bool two_byte = subset_charset_ranges.complete (glyph); | 
| 492 |  | 
| 493 |     size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1); | 
| 494 |     if (!two_byte) | 
| 495 |       size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length; | 
| 496 |     else | 
| 497 |       size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.length; | 
| 498 |  | 
| 499 |     if (size0 < size_ranges) | 
| 500 |       subset_charset_format = 0; | 
| 501 |     else if (!two_byte) | 
| 502 |       subset_charset_format = 1; | 
| 503 |     else | 
| 504 |       subset_charset_format = 2; | 
| 505 |   } | 
| 506 |  | 
| 507 |   bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) | 
| 508 |   { | 
| 509 |     sidmap.reset (); | 
| 510 |  | 
| 511 |     for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) | 
| 512 |     { | 
| 513 |       unsigned int sid = acc.topDict.nameSIDs[i]; | 
| 514 |       if (sid != CFF_UNDEF_SID) | 
| 515 |       { | 
| 516 | 	(void)sidmap.add (sid); | 
| 517 | 	topDictModSIDs[i] = sidmap[sid]; | 
| 518 |       } | 
| 519 |     } | 
| 520 |  | 
| 521 |     if (acc.fdArray != &Null (CFF1FDArray)) | 
| 522 |       for (unsigned int i = 0; i < orig_fdcount; i++) | 
| 523 | 	if (fdmap.has (i)) | 
| 524 | 	  (void)sidmap.add (acc.fontDicts[i].fontName); | 
| 525 |  | 
| 526 |     return true; | 
| 527 |   } | 
| 528 |  | 
| 529 |   bool create (const OT::cff1::accelerator_subset_t &acc, | 
| 530 | 	       hb_subset_plan_t *plan) | 
| 531 |   { | 
| 532 |     /* make sure notdef is first */ | 
| 533 |     hb_codepoint_t old_glyph; | 
| 534 |     if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; | 
| 535 |  | 
| 536 |     num_glyphs = plan->num_output_glyphs (); | 
| 537 |     orig_fdcount = acc.fdCount; | 
| 538 |     drop_hints = plan->drop_hints; | 
| 539 |     desubroutinize = plan->desubroutinize; | 
| 540 |  | 
| 541 |     /* check whether the subset renumbers any glyph IDs */ | 
| 542 |     gid_renum = false; | 
| 543 |     for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++) | 
| 544 |     { | 
| 545 |       if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph)) | 
| 546 | 	continue; | 
| 547 |       if (new_glyph != old_glyph) { | 
| 548 | 	gid_renum = true; | 
| 549 | 	break; | 
| 550 |       } | 
| 551 |     } | 
| 552 |  | 
| 553 |     subset_charset = gid_renum || !acc.is_predef_charset (); | 
| 554 |     subset_encoding = !acc.is_CID() && !acc.is_predef_encoding (); | 
| 555 |  | 
| 556 |     /* top dict INDEX */ | 
| 557 |     { | 
| 558 |       /* Add encoding/charset to a (copy of) top dict as necessary */ | 
| 559 |       topdict_mod.init (&acc.topDict); | 
| 560 |       bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding)); | 
| 561 |       bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset)); | 
| 562 |       if (need_to_add_enc || need_to_add_set) | 
| 563 |       { | 
| 564 | 	if (need_to_add_enc) | 
| 565 | 	  topdict_mod.add_op (OpCode_Encoding); | 
| 566 | 	if (need_to_add_set) | 
| 567 | 	  topdict_mod.add_op (OpCode_charset); | 
| 568 |       } | 
| 569 |     } | 
| 570 |  | 
| 571 |     /* Determine re-mapping of font index as fdmap among other info */ | 
| 572 |     if (acc.fdSelect != &Null (CFF1FDSelect)) | 
| 573 |     { | 
| 574 | 	if (unlikely (!hb_plan_subset_cff_fdselect (plan, | 
| 575 | 				  orig_fdcount, | 
| 576 | 				  *acc.fdSelect, | 
| 577 | 				  subset_fdcount, | 
| 578 | 				  info.fd_select.size, | 
| 579 | 				  subset_fdselect_format, | 
| 580 | 				  subset_fdselect_ranges, | 
| 581 | 				  fdmap))) | 
| 582 | 	return false; | 
| 583 |     } | 
| 584 |     else | 
| 585 |       fdmap.identity (1); | 
| 586 |  | 
| 587 |     /* remove unused SIDs & reassign SIDs */ | 
| 588 |     { | 
| 589 |       /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */ | 
| 590 |       if (unlikely (!collect_sids_in_dicts (acc))) | 
| 591 | 	return false; | 
| 592 |       if (unlikely (sidmap.get_population () > 0x8000))	/* assumption: a dict won't reference that many strings */ | 
| 593 | 	return false; | 
| 594 |  | 
| 595 |       if (subset_charset) plan_subset_charset (acc, plan); | 
| 596 |  | 
| 597 |       topdict_mod.reassignSIDs (sidmap); | 
| 598 |     } | 
| 599 |  | 
| 600 |     if (desubroutinize) | 
| 601 |     { | 
| 602 |       /* Flatten global & local subrs */ | 
| 603 |       subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, OpCode_endchar> | 
| 604 | 		    flattener(acc, plan); | 
| 605 |       if (!flattener.flatten (subset_charstrings)) | 
| 606 | 	return false; | 
| 607 |     } | 
| 608 |     else | 
| 609 |     { | 
| 610 |       cff1_subr_subsetter_t       subr_subsetter (acc, plan); | 
| 611 |  | 
| 612 |       /* Subset subrs: collect used subroutines, leaving all unused ones behind */ | 
| 613 |       if (!subr_subsetter.subset ()) | 
| 614 | 	return false; | 
| 615 |  | 
| 616 |       /* encode charstrings, global subrs, local subrs with new subroutine numbers */ | 
| 617 |       if (!subr_subsetter.encode_charstrings (subset_charstrings)) | 
| 618 | 	return false; | 
| 619 |  | 
| 620 |       if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) | 
| 621 | 	return false; | 
| 622 |  | 
| 623 |       /* local subrs */ | 
| 624 |       if (!subset_localsubrs.resize (orig_fdcount)) | 
| 625 | 	return false; | 
| 626 |       for (unsigned int fd = 0; fd < orig_fdcount; fd++) | 
| 627 |       { | 
| 628 | 	subset_localsubrs[fd].init (); | 
| 629 | 	if (fdmap.has (fd)) | 
| 630 | 	{ | 
| 631 | 	  if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) | 
| 632 | 	    return false; | 
| 633 | 	} | 
| 634 |       } | 
| 635 |     } | 
| 636 |  | 
| 637 |     /* Encoding */ | 
| 638 |     if (subset_encoding) | 
| 639 |       plan_subset_encoding (acc, plan); | 
| 640 |  | 
| 641 |     /* private dicts & local subrs */ | 
| 642 |     if (!acc.is_CID ()) | 
| 643 |       fontdicts_mod.push (cff1_font_dict_values_mod_t ()); | 
| 644 |     else | 
| 645 |     { | 
| 646 |       + hb_iter (acc.fontDicts) | 
| 647 |       | hb_filter ([&] (const cff1_font_dict_values_t &_) | 
| 648 | 	{ return fdmap.has (&_ - &acc.fontDicts[0]); } ) | 
| 649 |       | hb_map ([&] (const cff1_font_dict_values_t &_) | 
| 650 | 	{ | 
| 651 | 	  cff1_font_dict_values_mod_t mod; | 
| 652 | 	  mod.init (&_, sidmap[_.fontName]); | 
| 653 | 	  return mod; | 
| 654 | 	}) | 
| 655 |       | hb_sink (fontdicts_mod) | 
| 656 |       ; | 
| 657 |     } | 
| 658 |  | 
| 659 |     return ((subset_charstrings.length == plan->num_output_glyphs ()) | 
| 660 | 	   && (fontdicts_mod.length == subset_fdcount)); | 
| 661 |   } | 
| 662 |  | 
| 663 |   cff1_top_dict_values_mod_t	topdict_mod; | 
| 664 |   cff1_sub_table_info_t		info; | 
| 665 |  | 
| 666 |   unsigned int    num_glyphs; | 
| 667 |   unsigned int    orig_fdcount; | 
| 668 |   unsigned int    subset_fdcount; | 
| 669 |   unsigned int    subset_fdselect_format; | 
| 670 |   hb_vector_t<code_pair_t>   subset_fdselect_ranges; | 
| 671 |  | 
| 672 |   /* font dict index remap table from fullset FDArray to subset FDArray. | 
| 673 |    * set to CFF_UNDEF_CODE if excluded from subset */ | 
| 674 |   hb_inc_bimap_t   fdmap; | 
| 675 |  | 
| 676 |   str_buff_vec_t		subset_charstrings; | 
| 677 |   str_buff_vec_t		subset_globalsubrs; | 
| 678 |   hb_vector_t<str_buff_vec_t>	subset_localsubrs; | 
| 679 |   hb_vector_t<cff1_font_dict_values_mod_t>  fontdicts_mod; | 
| 680 |  | 
| 681 |   bool		drop_hints; | 
| 682 |  | 
| 683 |   bool		gid_renum; | 
| 684 |   bool		subset_encoding; | 
| 685 |   uint8_t	subset_enc_format; | 
| 686 |   unsigned int	subset_enc_num_codes; | 
| 687 |   range_list_t	subset_enc_code_ranges; | 
| 688 |   hb_vector_t<code_pair_t>  subset_enc_supp_codes; | 
| 689 |  | 
| 690 |   uint8_t	subset_charset_format; | 
| 691 |   range_list_t	subset_charset_ranges; | 
| 692 |   bool		subset_charset; | 
| 693 |  | 
| 694 |   remap_sid_t	sidmap; | 
| 695 |   unsigned int	topDictModSIDs[name_dict_values_t::ValCount]; | 
| 696 |  | 
| 697 |   bool		desubroutinize; | 
| 698 | }; | 
| 699 |  | 
| 700 | static bool _serialize_cff1 (hb_serialize_context_t *c, | 
| 701 | 			     cff_subset_plan &plan, | 
| 702 | 			     const OT::cff1::accelerator_subset_t  &acc, | 
| 703 | 			     unsigned int num_glyphs) | 
| 704 | { | 
| 705 |   /* private dicts & local subrs */ | 
| 706 |   for (int i = (int)acc.privateDicts.length; --i >= 0 ;) | 
| 707 |   { | 
| 708 |     if (plan.fdmap.has (i)) | 
| 709 |     { | 
| 710 |       objidx_t	subrs_link = 0; | 
| 711 |       if (plan.subset_localsubrs[i].length > 0) | 
| 712 |       { | 
| 713 | 	CFF1Subrs *dest = c->start_embed <CFF1Subrs> (); | 
| 714 | 	if (unlikely (!dest)) return false; | 
| 715 | 	c->push (); | 
| 716 | 	if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i]))) | 
| 717 | 	  subrs_link = c->pop_pack (); | 
| 718 | 	else | 
| 719 | 	{ | 
| 720 | 	  c->pop_discard (); | 
| 721 | 	  return false; | 
| 722 | 	} | 
| 723 |       } | 
| 724 |  | 
| 725 |       PrivateDict *pd = c->start_embed<PrivateDict> (); | 
| 726 |       if (unlikely (!pd)) return false; | 
| 727 |       c->push (); | 
| 728 |       cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); | 
| 729 |       /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ | 
| 730 |       if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) | 
| 731 |       { | 
| 732 | 	unsigned fd = plan.fdmap[i]; | 
| 733 | 	plan.fontdicts_mod[fd].privateDictInfo.size = c->length (); | 
| 734 | 	plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack (); | 
| 735 |       } | 
| 736 |       else | 
| 737 |       { | 
| 738 | 	c->pop_discard (); | 
| 739 | 	return false; | 
| 740 |       } | 
| 741 |     } | 
| 742 |   } | 
| 743 |  | 
| 744 |   if (!acc.is_CID ()) | 
| 745 |     plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; | 
| 746 |  | 
| 747 |   /* CharStrings */ | 
| 748 |   { | 
| 749 |     CFF1CharStrings  *cs = c->start_embed<CFF1CharStrings> (); | 
| 750 |     if (unlikely (!cs)) return false; | 
| 751 |     c->push (); | 
| 752 |     if (likely (cs->serialize (c, plan.subset_charstrings))) | 
| 753 |       plan.info.char_strings_link = c->pop_pack (); | 
| 754 |     else | 
| 755 |     { | 
| 756 |       c->pop_discard (); | 
| 757 |       return false; | 
| 758 |     } | 
| 759 |   } | 
| 760 |  | 
| 761 |   /* FDArray (FD Index) */ | 
| 762 |   if (acc.fdArray != &Null (CFF1FDArray)) | 
| 763 |   { | 
| 764 |     CFF1FDArray *fda = c->start_embed<CFF1FDArray> (); | 
| 765 |     if (unlikely (!fda)) return false; | 
| 766 |     c->push (); | 
| 767 |     cff1_font_dict_op_serializer_t  fontSzr; | 
| 768 |     auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod)); | 
| 769 |     if (likely (fda->serialize (c, it, fontSzr))) | 
| 770 |       plan.info.fd_array_link = c->pop_pack (false); | 
| 771 |     else | 
| 772 |     { | 
| 773 |       c->pop_discard (); | 
| 774 |       return false; | 
| 775 |     } | 
| 776 |   } | 
| 777 |  | 
| 778 |   /* FDSelect */ | 
| 779 |   if (acc.fdSelect != &Null (CFF1FDSelect)) | 
| 780 |   { | 
| 781 |     c->push (); | 
| 782 |     if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount, | 
| 783 | 					   plan.subset_fdselect_format, plan.info.fd_select.size, | 
| 784 | 					   plan.subset_fdselect_ranges))) | 
| 785 |       plan.info.fd_select.link = c->pop_pack (); | 
| 786 |     else | 
| 787 |     { | 
| 788 |       c->pop_discard (); | 
| 789 |       return false; | 
| 790 |     } | 
| 791 |   } | 
| 792 |  | 
| 793 |   /* Charset */ | 
| 794 |   if (plan.subset_charset) | 
| 795 |   { | 
| 796 |     Charset *dest = c->start_embed<Charset> (); | 
| 797 |     if (unlikely (!dest)) return false; | 
| 798 |     c->push (); | 
| 799 |     if (likely (dest->serialize (c, | 
| 800 | 				 plan.subset_charset_format, | 
| 801 | 				 plan.num_glyphs, | 
| 802 | 				 plan.subset_charset_ranges))) | 
| 803 |       plan.info.charset_link = c->pop_pack (); | 
| 804 |     else | 
| 805 |     { | 
| 806 |       c->pop_discard (); | 
| 807 |       return false; | 
| 808 |     } | 
| 809 |   } | 
| 810 |  | 
| 811 |   /* Encoding */ | 
| 812 |   if (plan.subset_encoding) | 
| 813 |   { | 
| 814 |     Encoding *dest = c->start_embed<Encoding> (); | 
| 815 |     if (unlikely (!dest)) return false; | 
| 816 |     c->push (); | 
| 817 |     if (likely (dest->serialize (c, | 
| 818 | 				 plan.subset_enc_format, | 
| 819 | 				 plan.subset_enc_num_codes, | 
| 820 | 				 plan.subset_enc_code_ranges, | 
| 821 | 				 plan.subset_enc_supp_codes))) | 
| 822 |       plan.info.encoding_link = c->pop_pack (); | 
| 823 |     else | 
| 824 |     { | 
| 825 |       c->pop_discard (); | 
| 826 |       return false; | 
| 827 |     } | 
| 828 |   } | 
| 829 |  | 
| 830 |   /* global subrs */ | 
| 831 |   { | 
| 832 |     c->push (); | 
| 833 |     CFF1Subrs *dest = c->start_embed <CFF1Subrs> (); | 
| 834 |     if (unlikely (!dest)) return false; | 
| 835 |     if (likely (dest->serialize (c, plan.subset_globalsubrs))) | 
| 836 |       c->pop_pack (); | 
| 837 |     else | 
| 838 |     { | 
| 839 |       c->pop_discard (); | 
| 840 |       return false; | 
| 841 |     } | 
| 842 |   } | 
| 843 |  | 
| 844 |   /* String INDEX */ | 
| 845 |   { | 
| 846 |     CFF1StringIndex *dest = c->start_embed<CFF1StringIndex> (); | 
| 847 |     if (unlikely (!dest)) return false; | 
| 848 |     c->push (); | 
| 849 |     if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap))) | 
| 850 |       c->pop_pack (); | 
| 851 |     else | 
| 852 |     { | 
| 853 |       c->pop_discard (); | 
| 854 |       return false; | 
| 855 |     } | 
| 856 |   } | 
| 857 |  | 
| 858 |   OT::cff1 *cff = c->allocate_min<OT::cff1> (); | 
| 859 |   if (unlikely (!cff)) | 
| 860 |     return false; | 
| 861 |  | 
| 862 |   /* header */ | 
| 863 |   cff->version.major = 0x01; | 
| 864 |   cff->version.minor = 0x00; | 
| 865 |   cff->nameIndex = cff->min_size; | 
| 866 |   cff->offSize = 4; /* unused? */ | 
| 867 |  | 
| 868 |   /* name INDEX */ | 
| 869 |   if (unlikely (!(*acc.nameIndex).copy (c))) return false; | 
| 870 |  | 
| 871 |   /* top dict INDEX */ | 
| 872 |   { | 
| 873 |     /* serialize singleton TopDict */ | 
| 874 |     TopDict *top = c->start_embed<TopDict> (); | 
| 875 |     if (!top) return false; | 
| 876 |     c->push (); | 
| 877 |     cff1_top_dict_op_serializer_t topSzr; | 
| 878 |     unsigned top_size = 0; | 
| 879 |     top_dict_modifiers_t  modifier (plan.info, plan.topDictModSIDs); | 
| 880 |     if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier))) | 
| 881 |     { | 
| 882 |       top_size = c->length (); | 
| 883 |       c->pop_pack (false); | 
| 884 |     } | 
| 885 |     else | 
| 886 |     { | 
| 887 |       c->pop_discard (); | 
| 888 |       return false; | 
| 889 |     } | 
| 890 |     /* serialize INDEX header for above */ | 
| 891 |     CFF1Index *dest = c->start_embed<CFF1Index> (); | 
| 892 |     if (!dest) return false; | 
| 893 |     return dest->serialize_header (c, hb_iter (hb_array_t<unsigned> (&top_size, 1))); | 
| 894 |   } | 
| 895 | } | 
| 896 |  | 
| 897 | static bool | 
| 898 | _hb_subset_cff1 (const OT::cff1::accelerator_subset_t  &acc, | 
| 899 | 		hb_subset_context_t	*c) | 
| 900 | { | 
| 901 |   cff_subset_plan cff_plan; | 
| 902 |  | 
| 903 |   if (unlikely (!cff_plan.create (acc, c->plan))) | 
| 904 |   { | 
| 905 |     DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan." ); | 
| 906 |     return false; | 
| 907 |   } | 
| 908 |  | 
| 909 |   return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ()); | 
| 910 | } | 
| 911 |  | 
| 912 | /** | 
| 913 |  * hb_subset_cff1: | 
| 914 |  * Subsets the CFF table according to a provided plan. | 
| 915 |  * | 
| 916 |  * Return value: subsetted cff table. | 
| 917 |  **/ | 
| 918 | bool | 
| 919 | hb_subset_cff1 (hb_subset_context_t *c) | 
| 920 | { | 
| 921 |   OT::cff1::accelerator_subset_t acc; | 
| 922 |   acc.init (c->plan->source); | 
| 923 |   bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c); | 
| 924 |   acc.fini (); | 
| 925 |  | 
| 926 |   return result; | 
| 927 | } | 
| 928 |  | 
| 929 |  | 
| 930 | #endif | 
| 931 |  |