Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-subset-cff2.cc @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 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-cff2-table.hh" | |
| 33 #include "hb-set.h" | |
| 34 #include "hb-subset-cff2.hh" | |
| 35 #include "hb-subset-plan.hh" | |
| 36 #include "hb-subset-cff-common.hh" | |
| 37 #include "hb-cff2-interp-cs.hh" | |
| 38 | |
| 39 using namespace CFF; | |
| 40 | |
| 41 struct cff2_sub_table_info_t : cff_sub_table_info_t | |
| 42 { | |
| 43 cff2_sub_table_info_t () | |
| 44 : cff_sub_table_info_t (), | |
| 45 var_store_link (0) | |
| 46 {} | |
| 47 | |
| 48 objidx_t var_store_link; | |
| 49 }; | |
| 50 | |
| 51 struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> | |
| 52 { | |
| 53 bool serialize (hb_serialize_context_t *c, | |
| 54 const op_str_t &opstr, | |
| 55 const cff2_sub_table_info_t &info) const | |
| 56 { | |
| 57 TRACE_SERIALIZE (this); | |
| 58 | |
| 59 switch (opstr.op) | |
| 60 { | |
| 61 case OpCode_vstore: | |
| 62 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); | |
| 63 | |
| 64 default: | |
| 65 return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); | |
| 66 } | |
| 67 } | |
| 68 }; | |
| 69 | |
| 70 struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> | |
| 71 { | |
| 72 static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) | |
| 73 { | |
| 74 switch (op) | |
| 75 { | |
| 76 case OpCode_return: | |
| 77 case OpCode_endchar: | |
| 78 /* dummy opcodes in CFF2. ignore */ | |
| 79 break; | |
| 80 | |
| 81 case OpCode_hstem: | |
| 82 case OpCode_hstemhm: | |
| 83 case OpCode_vstem: | |
| 84 case OpCode_vstemhm: | |
| 85 case OpCode_hintmask: | |
| 86 case OpCode_cntrmask: | |
| 87 if (param.drop_hints) | |
| 88 { | |
| 89 env.clear_args (); | |
| 90 return; | |
| 91 } | |
| 92 HB_FALLTHROUGH; | |
| 93 | |
| 94 default: | |
| 95 SUPER::flush_args_and_op (op, env, param); | |
| 96 break; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) | |
| 101 { | |
| 102 for (unsigned int i = 0; i < env.argStack.get_count ();) | |
| 103 { | |
| 104 const blend_arg_t &arg = env.argStack[i]; | |
| 105 if (arg.blending ()) | |
| 106 { | |
| 107 if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues)))) | |
| 108 { | |
| 109 env.set_error (); | |
| 110 return; | |
| 111 } | |
| 112 flatten_blends (arg, i, env, param); | |
| 113 i += arg.numValues; | |
| 114 } | |
| 115 else | |
| 116 { | |
| 117 str_encoder_t encoder (param.flatStr); | |
| 118 encoder.encode_num (arg); | |
| 119 i++; | |
| 120 } | |
| 121 } | |
| 122 SUPER::flush_args (env, param); | |
| 123 } | |
| 124 | |
| 125 static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) | |
| 126 { | |
| 127 /* flatten the default values */ | |
| 128 str_encoder_t encoder (param.flatStr); | |
| 129 for (unsigned int j = 0; j < arg.numValues; j++) | |
| 130 { | |
| 131 const blend_arg_t &arg1 = env.argStack[i + j]; | |
| 132 if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && | |
| 133 (arg1.deltas.length == env.get_region_count ()))))) | |
| 134 { | |
| 135 env.set_error (); | |
| 136 return; | |
| 137 } | |
| 138 encoder.encode_num (arg1); | |
| 139 } | |
| 140 /* flatten deltas for each value */ | |
| 141 for (unsigned int j = 0; j < arg.numValues; j++) | |
| 142 { | |
| 143 const blend_arg_t &arg1 = env.argStack[i + j]; | |
| 144 for (unsigned int k = 0; k < arg1.deltas.length; k++) | |
| 145 encoder.encode_num (arg1.deltas[k]); | |
| 146 } | |
| 147 /* flatten the number of values followed by blend operator */ | |
| 148 encoder.encode_int (arg.numValues); | |
| 149 encoder.encode_op (OpCode_blendcs); | |
| 150 } | |
| 151 | |
| 152 static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) | |
| 153 { | |
| 154 switch (op) | |
| 155 { | |
| 156 case OpCode_return: | |
| 157 case OpCode_endchar: | |
| 158 return; | |
| 159 default: | |
| 160 str_encoder_t encoder (param.flatStr); | |
| 161 encoder.encode_op (op); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 private: | |
| 166 typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER; | |
| 167 typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET; | |
| 168 }; | |
| 169 | |
| 170 struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> | |
| 171 { | |
| 172 static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param) | |
| 173 { | |
| 174 switch (op) { | |
| 175 | |
| 176 case OpCode_return: | |
| 177 param.current_parsed_str->set_parsed (); | |
| 178 env.return_from_subr (); | |
| 179 param.set_current_str (env, false); | |
| 180 break; | |
| 181 | |
| 182 case OpCode_endchar: | |
| 183 param.current_parsed_str->set_parsed (); | |
| 184 SUPER::process_op (op, env, param); | |
| 185 break; | |
| 186 | |
| 187 case OpCode_callsubr: | |
| 188 process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); | |
| 189 break; | |
| 190 | |
| 191 case OpCode_callgsubr: | |
| 192 process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); | |
| 193 break; | |
| 194 | |
| 195 default: | |
| 196 SUPER::process_op (op, env, param); | |
| 197 param.current_parsed_str->add_op (op, env.str_ref); | |
| 198 break; | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 protected: | |
| 203 static void process_call_subr (op_code_t op, cs_type_t type, | |
| 204 cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, | |
| 205 cff2_biased_subrs_t& subrs, hb_set_t *closure) | |
| 206 { | |
| 207 byte_str_ref_t str_ref = env.str_ref; | |
| 208 env.call_subr (subrs, type); | |
| 209 param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); | |
| 210 closure->add (env.context.subr_num); | |
| 211 param.set_current_str (env, true); | |
| 212 } | |
| 213 | |
| 214 private: | |
| 215 typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER; | |
| 216 }; | |
| 217 | |
| 218 struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t> | |
| 219 { | |
| 220 cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) | |
| 221 : subr_subsetter_t (acc_, plan_) {} | |
| 222 | |
| 223 static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) | |
| 224 { | |
| 225 /* vsindex is inserted at the beginning of the charstring as necessary */ | |
| 226 if (env.seen_vsindex ()) | |
| 227 { | |
| 228 number_t ivs; | |
| 229 ivs.set_int ((int)env.get_ivs ()); | |
| 230 charstring.set_prefix (ivs, OpCode_vsindexcs); | |
| 231 } | |
| 232 } | |
| 233 }; | |
| 234 | |
| 235 struct cff2_subset_plan { | |
| 236 | |
| 237 bool create (const OT::cff2::accelerator_subset_t &acc, | |
| 238 hb_subset_plan_t *plan) | |
| 239 { | |
| 240 orig_fdcount = acc.fdArray->count; | |
| 241 | |
| 242 drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; | |
| 243 desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; | |
| 244 | |
| 245 if (desubroutinize) | |
| 246 { | |
| 247 /* Flatten global & local subrs */ | |
| 248 subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t> | |
| 249 flattener(acc, plan); | |
| 250 if (!flattener.flatten (subset_charstrings)) | |
| 251 return false; | |
| 252 } | |
| 253 else | |
| 254 { | |
| 255 cff2_subr_subsetter_t subr_subsetter (acc, plan); | |
| 256 | |
| 257 /* Subset subrs: collect used subroutines, leaving all unused ones behind */ | |
| 258 if (!subr_subsetter.subset ()) | |
| 259 return false; | |
| 260 | |
| 261 /* encode charstrings, global subrs, local subrs with new subroutine numbers */ | |
| 262 if (!subr_subsetter.encode_charstrings (subset_charstrings)) | |
| 263 return false; | |
| 264 | |
| 265 if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) | |
| 266 return false; | |
| 267 | |
| 268 /* local subrs */ | |
| 269 if (!subset_localsubrs.resize (orig_fdcount)) | |
| 270 return false; | |
| 271 for (unsigned int fd = 0; fd < orig_fdcount; fd++) | |
| 272 { | |
| 273 subset_localsubrs[fd].init (); | |
| 274 if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) | |
| 275 return false; | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 /* FDSelect */ | |
| 280 if (acc.fdSelect != &Null (CFF2FDSelect)) | |
| 281 { | |
| 282 if (unlikely (!hb_plan_subset_cff_fdselect (plan, | |
| 283 orig_fdcount, | |
| 284 *(const FDSelect *)acc.fdSelect, | |
| 285 subset_fdcount, | |
| 286 subset_fdselect_size, | |
| 287 subset_fdselect_format, | |
| 288 subset_fdselect_ranges, | |
| 289 fdmap))) | |
| 290 return false; | |
| 291 } | |
| 292 else | |
| 293 fdmap.identity (1); | |
| 294 | |
| 295 return true; | |
| 296 } | |
| 297 | |
| 298 cff2_sub_table_info_t info; | |
| 299 | |
| 300 unsigned int orig_fdcount = 0; | |
| 301 unsigned int subset_fdcount = 1; | |
| 302 unsigned int subset_fdselect_size = 0; | |
| 303 unsigned int subset_fdselect_format = 0; | |
| 304 hb_vector_t<code_pair_t> subset_fdselect_ranges; | |
| 305 | |
| 306 hb_inc_bimap_t fdmap; | |
| 307 | |
| 308 str_buff_vec_t subset_charstrings; | |
| 309 str_buff_vec_t subset_globalsubrs; | |
| 310 hb_vector_t<str_buff_vec_t> subset_localsubrs; | |
| 311 | |
| 312 bool drop_hints = false; | |
| 313 bool desubroutinize = false; | |
| 314 }; | |
| 315 | |
| 316 static bool _serialize_cff2 (hb_serialize_context_t *c, | |
| 317 cff2_subset_plan &plan, | |
| 318 const OT::cff2::accelerator_subset_t &acc, | |
| 319 unsigned int num_glyphs) | |
| 320 { | |
| 321 /* private dicts & local subrs */ | |
| 322 hb_vector_t<table_info_t> private_dict_infos; | |
| 323 if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; | |
| 324 | |
| 325 for (int i = (int)acc.privateDicts.length; --i >= 0 ;) | |
| 326 { | |
| 327 if (plan.fdmap.has (i)) | |
| 328 { | |
| 329 objidx_t subrs_link = 0; | |
| 330 | |
| 331 if (plan.subset_localsubrs[i].length > 0) | |
| 332 { | |
| 333 CFF2Subrs *dest = c->start_embed <CFF2Subrs> (); | |
| 334 if (unlikely (!dest)) return false; | |
| 335 c->push (); | |
| 336 if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) | |
| 337 subrs_link = c->pop_pack (false); | |
| 338 else | |
| 339 { | |
| 340 c->pop_discard (); | |
| 341 return false; | |
| 342 } | |
| 343 } | |
| 344 PrivateDict *pd = c->start_embed<PrivateDict> (); | |
| 345 if (unlikely (!pd)) return false; | |
| 346 c->push (); | |
| 347 cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); | |
| 348 if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) | |
| 349 { | |
| 350 unsigned fd = plan.fdmap[i]; | |
| 351 private_dict_infos[fd].size = c->length (); | |
| 352 private_dict_infos[fd].link = c->pop_pack (); | |
| 353 } | |
| 354 else | |
| 355 { | |
| 356 c->pop_discard (); | |
| 357 return false; | |
| 358 } | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 /* CharStrings */ | |
| 363 { | |
| 364 c->push (); | |
| 365 | |
| 366 unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings); | |
| 367 if (unlikely (!c->start_zerocopy (total_size))) | |
| 368 return false; | |
| 369 | |
| 370 CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> (); | |
| 371 if (unlikely (!cs)) return false; | |
| 372 | |
| 373 if (likely (cs->serialize (c, plan.subset_charstrings))) | |
| 374 plan.info.char_strings_link = c->pop_pack (false); | |
| 375 else | |
| 376 { | |
| 377 c->pop_discard (); | |
| 378 return false; | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 /* FDSelect */ | |
| 383 if (acc.fdSelect != &Null (CFF2FDSelect)) | |
| 384 { | |
| 385 c->push (); | |
| 386 if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, | |
| 387 plan.orig_fdcount, | |
| 388 plan.subset_fdselect_format, plan.subset_fdselect_size, | |
| 389 plan.subset_fdselect_ranges))) | |
| 390 plan.info.fd_select.link = c->pop_pack (); | |
| 391 else | |
| 392 { | |
| 393 c->pop_discard (); | |
| 394 return false; | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 /* FDArray (FD Index) */ | |
| 399 { | |
| 400 c->push (); | |
| 401 CFF2FDArray *fda = c->start_embed<CFF2FDArray> (); | |
| 402 if (unlikely (!fda)) return false; | |
| 403 cff_font_dict_op_serializer_t fontSzr; | |
| 404 auto it = | |
| 405 + hb_zip (+ hb_iter (acc.fontDicts) | |
| 406 | hb_filter ([&] (const cff2_font_dict_values_t &_) | |
| 407 { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), | |
| 408 hb_iter (private_dict_infos)) | |
| 409 ; | |
| 410 if (unlikely (!fda->serialize (c, it, fontSzr))) return false; | |
| 411 plan.info.fd_array_link = c->pop_pack (false); | |
| 412 } | |
| 413 | |
| 414 /* variation store */ | |
| 415 if (acc.varStore != &Null (CFF2VariationStore)) | |
| 416 { | |
| 417 c->push (); | |
| 418 CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> (); | |
| 419 if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; | |
| 420 plan.info.var_store_link = c->pop_pack (false); | |
| 421 } | |
| 422 | |
| 423 OT::cff2 *cff2 = c->allocate_min<OT::cff2> (); | |
| 424 if (unlikely (!cff2)) return false; | |
| 425 | |
| 426 /* header */ | |
| 427 cff2->version.major = 0x02; | |
| 428 cff2->version.minor = 0x00; | |
| 429 cff2->topDict = OT::cff2::static_size; | |
| 430 | |
| 431 /* top dict */ | |
| 432 { | |
| 433 TopDict &dict = cff2 + cff2->topDict; | |
| 434 cff2_top_dict_op_serializer_t topSzr; | |
| 435 if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false; | |
| 436 cff2->topDictSize = c->head - (const char *)&dict; | |
| 437 } | |
| 438 | |
| 439 /* global subrs */ | |
| 440 { | |
| 441 CFF2Subrs *dest = c->start_embed <CFF2Subrs> (); | |
| 442 if (unlikely (!dest)) return false; | |
| 443 return dest->serialize (c, plan.subset_globalsubrs); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 static bool | |
| 448 _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, | |
| 449 hb_subset_context_t *c) | |
| 450 { | |
| 451 cff2_subset_plan cff2_plan; | |
| 452 | |
| 453 if (unlikely (!cff2_plan.create (acc, c->plan))) return false; | |
| 454 return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); | |
| 455 } | |
| 456 | |
| 457 bool | |
| 458 hb_subset_cff2 (hb_subset_context_t *c) | |
| 459 { | |
| 460 OT::cff2::accelerator_subset_t acc (c->plan->source); | |
| 461 return acc.is_valid () && _hb_subset_cff2 (acc, c); | |
| 462 } | |
| 463 | |
| 464 #endif |
