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 #ifndef HB_SUBSET_CFF_COMMON_HH 28 #define HB_SUBSET_CFF_COMMON_HH 29 30 #include "hb.hh" 31 32 #include "hb-subset-plan.hh" 33 #include "hb-cff-interp-cs-common.hh" 34 35 namespace CFF { 36 37 /* Used for writing a temporary charstring */ 38 struct StrEncoder 39 { StrEncoderCFF::StrEncoder40 StrEncoder (StrBuff &buff_) 41 : buff (buff_), error (false) {} 42 resetCFF::StrEncoder43 void reset () { buff.resize (0); } 44 encode_byteCFF::StrEncoder45 void encode_byte (unsigned char b) 46 { 47 if (unlikely (buff.push ((const char)b) == &Crap(char))) 48 set_error (); 49 } 50 encode_intCFF::StrEncoder51 void encode_int (int v) 52 { 53 if ((-1131 <= v) && (v <= 1131)) 54 { 55 if ((-107 <= v) && (v <= 107)) 56 encode_byte (v + 139); 57 else if (v > 0) 58 { 59 v -= 108; 60 encode_byte ((v >> 8) + OpCode_TwoBytePosInt0); 61 encode_byte (v & 0xFF); 62 } 63 else 64 { 65 v = -v - 108; 66 encode_byte ((v >> 8) + OpCode_TwoByteNegInt0); 67 encode_byte (v & 0xFF); 68 } 69 } 70 else 71 { 72 if (unlikely (v < -32768)) 73 v = -32768; 74 else if (unlikely (v > 32767)) 75 v = 32767; 76 encode_byte (OpCode_shortint); 77 encode_byte ((v >> 8) & 0xFF); 78 encode_byte (v & 0xFF); 79 } 80 } 81 encode_numCFF::StrEncoder82 void encode_num (const Number& n) 83 { 84 if (n.in_int_range ()) 85 { 86 encode_int (n.to_int ()); 87 } 88 else 89 { 90 int32_t v = n.to_fixed (); 91 encode_byte (OpCode_fixedcs); 92 encode_byte ((v >> 24) & 0xFF); 93 encode_byte ((v >> 16) & 0xFF); 94 encode_byte ((v >> 8) & 0xFF); 95 encode_byte (v & 0xFF); 96 } 97 } 98 encode_opCFF::StrEncoder99 void encode_op (OpCode op) 100 { 101 if (Is_OpCode_ESC (op)) 102 { 103 encode_byte (OpCode_escape); 104 encode_byte (Unmake_OpCode_ESC (op)); 105 } 106 else 107 encode_byte (op); 108 } 109 copy_strCFF::StrEncoder110 void copy_str (const ByteStr &str) 111 { 112 unsigned int offset = buff.len; 113 buff.resize (offset + str.len); 114 if (unlikely (buff.len < offset + str.len)) 115 { 116 set_error (); 117 return; 118 } 119 memcpy (&buff[offset], &str.str[0], str.len); 120 } 121 is_errorCFF::StrEncoder122 bool is_error () const { return error; } 123 124 protected: set_errorCFF::StrEncoder125 void set_error () { error = true; } 126 127 StrBuff &buff; 128 bool error; 129 }; 130 131 struct CFFSubTableOffsets { CFFSubTableOffsetsCFF::CFFSubTableOffsets132 CFFSubTableOffsets () : privateDictsOffset (0) 133 { 134 topDictInfo.init (); 135 FDSelectInfo.init (); 136 FDArrayInfo.init (); 137 charStringsInfo.init (); 138 globalSubrsInfo.init (); 139 localSubrsInfos.init (); 140 } 141 ~CFFSubTableOffsetsCFF::CFFSubTableOffsets142 ~CFFSubTableOffsets () { localSubrsInfos.fini (); } 143 144 TableInfo topDictInfo; 145 TableInfo FDSelectInfo; 146 TableInfo FDArrayInfo; 147 TableInfo charStringsInfo; 148 unsigned int privateDictsOffset; 149 TableInfo globalSubrsInfo; 150 hb_vector_t<TableInfo> localSubrsInfos; 151 }; 152 153 template <typename OPSTR=OpStr> 154 struct CFFTopDict_OpSerializer : OpSerializer 155 { serializeCFF::CFFTopDict_OpSerializer156 bool serialize (hb_serialize_context_t *c, 157 const OPSTR &opstr, 158 const CFFSubTableOffsets &offsets) const 159 { 160 TRACE_SERIALIZE (this); 161 162 switch (opstr.op) 163 { 164 case OpCode_CharStrings: 165 return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset)); 166 167 case OpCode_FDArray: 168 return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset)); 169 170 case OpCode_FDSelect: 171 return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset)); 172 173 default: 174 return_trace (copy_opstr (c, opstr)); 175 } 176 return_trace (true); 177 } 178 calculate_serialized_sizeCFF::CFFTopDict_OpSerializer179 unsigned int calculate_serialized_size (const OPSTR &opstr) const 180 { 181 switch (opstr.op) 182 { 183 case OpCode_CharStrings: 184 case OpCode_FDArray: 185 case OpCode_FDSelect: 186 return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); 187 188 default: 189 return opstr.str.len; 190 } 191 } 192 }; 193 194 struct CFFFontDict_OpSerializer : OpSerializer 195 { serializeCFF::CFFFontDict_OpSerializer196 bool serialize (hb_serialize_context_t *c, 197 const OpStr &opstr, 198 const TableInfo &privateDictInfo) const 199 { 200 TRACE_SERIALIZE (this); 201 202 if (opstr.op == OpCode_Private) 203 { 204 /* serialize the private dict size & offset as 2-byte & 4-byte integers */ 205 if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) || 206 !UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset))) 207 return_trace (false); 208 209 /* serialize the opcode */ 210 HBUINT8 *p = c->allocate_size<HBUINT8> (1); 211 if (unlikely (p == nullptr)) return_trace (false); 212 p->set (OpCode_Private); 213 214 return_trace (true); 215 } 216 else 217 { 218 HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len); 219 if (unlikely (d == nullptr)) return_trace (false); 220 memcpy (d, &opstr.str.str[0], opstr.str.len); 221 } 222 return_trace (true); 223 } 224 calculate_serialized_sizeCFF::CFFFontDict_OpSerializer225 unsigned int calculate_serialized_size (const OpStr &opstr) const 226 { 227 if (opstr.op == OpCode_Private) 228 return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); 229 else 230 return opstr.str.len; 231 } 232 }; 233 234 struct CFFPrivateDict_OpSerializer : OpSerializer 235 { CFFPrivateDict_OpSerializerCFF::CFFPrivateDict_OpSerializer236 CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_) 237 : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} 238 serializeCFF::CFFPrivateDict_OpSerializer239 bool serialize (hb_serialize_context_t *c, 240 const OpStr &opstr, 241 const unsigned int subrsOffset) const 242 { 243 TRACE_SERIALIZE (this); 244 245 if (drop_hints && DictOpSet::is_hint_op (opstr.op)) 246 return true; 247 if (opstr.op == OpCode_Subrs) 248 { 249 if (desubroutinize || (subrsOffset == 0)) 250 return_trace (true); 251 else 252 return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset)); 253 } 254 else 255 return_trace (copy_opstr (c, opstr)); 256 } 257 calculate_serialized_sizeCFF::CFFPrivateDict_OpSerializer258 unsigned int calculate_serialized_size (const OpStr &opstr, 259 bool has_localsubr=true) const 260 { 261 if (drop_hints && DictOpSet::is_hint_op (opstr.op)) 262 return 0; 263 if (opstr.op == OpCode_Subrs) 264 { 265 if (desubroutinize || !has_localsubr) 266 return 0; 267 else 268 return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op); 269 } 270 else 271 return opstr.str.len; 272 } 273 274 protected: 275 const bool desubroutinize; 276 const bool drop_hints; 277 }; 278 279 struct FlattenParam 280 { 281 StrBuff &flatStr; 282 bool drop_hints; 283 }; 284 285 template <typename ACC, typename ENV, typename OPSET> 286 struct SubrFlattener 287 { SubrFlattenerCFF::SubrFlattener288 SubrFlattener (const ACC &acc_, 289 const hb_vector_t<hb_codepoint_t> &glyphs_, 290 bool drop_hints_) 291 : acc (acc_), 292 glyphs (glyphs_), 293 drop_hints (drop_hints_) 294 {} 295 flattenCFF::SubrFlattener296 bool flatten (StrBuffArray &flat_charstrings) 297 { 298 if (!flat_charstrings.resize (glyphs.len)) 299 return false; 300 for (unsigned int i = 0; i < glyphs.len; i++) 301 flat_charstrings[i].init (); 302 for (unsigned int i = 0; i < glyphs.len; i++) 303 { 304 hb_codepoint_t glyph = glyphs[i]; 305 const ByteStr str = (*acc.charStrings)[glyph]; 306 unsigned int fd = acc.fdSelect->get_fd (glyph); 307 if (unlikely (fd >= acc.fdCount)) 308 return false; 309 CSInterpreter<ENV, OPSET, FlattenParam> interp; 310 interp.env.init (str, acc, fd); 311 FlattenParam param = { flat_charstrings[i], drop_hints }; 312 if (unlikely (!interp.interpret (param))) 313 return false; 314 } 315 return true; 316 } 317 318 const ACC &acc; 319 const hb_vector_t<hb_codepoint_t> &glyphs; 320 bool drop_hints; 321 }; 322 323 struct SubrClosures 324 { SubrClosuresCFF::SubrClosures325 SubrClosures () : valid (false), global_closure (nullptr) 326 { local_closures.init (); } 327 initCFF::SubrClosures328 void init (unsigned int fd_count) 329 { 330 valid = true; 331 global_closure = hb_set_create (); 332 if (global_closure == hb_set_get_empty ()) 333 valid = false; 334 if (!local_closures.resize (fd_count)) 335 valid = false; 336 337 for (unsigned int i = 0; i < local_closures.len; i++) 338 { 339 local_closures[i] = hb_set_create (); 340 if (local_closures[i] == hb_set_get_empty ()) 341 valid = false; 342 } 343 } 344 finiCFF::SubrClosures345 void fini () 346 { 347 hb_set_destroy (global_closure); 348 for (unsigned int i = 0; i < local_closures.len; i++) 349 hb_set_destroy (local_closures[i]); 350 local_closures.fini (); 351 } 352 resetCFF::SubrClosures353 void reset () 354 { 355 hb_set_clear (global_closure); 356 for (unsigned int i = 0; i < local_closures.len; i++) 357 hb_set_clear (local_closures[i]); 358 } 359 is_validCFF::SubrClosures360 bool is_valid () const { return valid; } 361 bool valid; 362 hb_set_t *global_closure; 363 hb_vector_t<hb_set_t *> local_closures; 364 }; 365 366 struct ParsedCSOp : OpStr 367 { initCFF::ParsedCSOp368 void init (unsigned int subr_num_ = 0) 369 { 370 OpStr::init (); 371 subr_num = subr_num_; 372 drop_flag = false; 373 keep_flag = false; 374 skip_flag = false; 375 } 376 finiCFF::ParsedCSOp377 void fini () { OpStr::fini (); } 378 for_dropCFF::ParsedCSOp379 bool for_drop () const { return drop_flag; } set_dropCFF::ParsedCSOp380 void set_drop () { if (!for_keep ()) drop_flag = true; } 381 for_keepCFF::ParsedCSOp382 bool for_keep () const { return keep_flag; } set_keepCFF::ParsedCSOp383 void set_keep () { keep_flag = true; } 384 for_skipCFF::ParsedCSOp385 bool for_skip () const { return skip_flag; } set_skipCFF::ParsedCSOp386 void set_skip () { skip_flag = true; } 387 388 unsigned int subr_num; 389 390 protected: 391 bool drop_flag : 1; 392 bool keep_flag : 1; 393 bool skip_flag : 1; 394 }; 395 396 struct ParsedCStr : ParsedValues<ParsedCSOp> 397 { initCFF::ParsedCStr398 void init () 399 { 400 SUPER::init (); 401 parsed = false; 402 hint_dropped = false; 403 has_prefix_ = false; 404 } 405 add_opCFF::ParsedCStr406 void add_op (OpCode op, const SubByteStr& substr) 407 { 408 if (!is_parsed ()) 409 SUPER::add_op (op, substr); 410 } 411 add_call_opCFF::ParsedCStr412 void add_call_op (OpCode op, const SubByteStr& substr, unsigned int subr_num) 413 { 414 if (!is_parsed ()) 415 { 416 unsigned int parsed_len = get_count (); 417 if (likely (parsed_len > 0)) 418 values[parsed_len-1].set_skip (); 419 420 ParsedCSOp val; 421 val.init (subr_num); 422 SUPER::add_op (op, substr, val); 423 } 424 } 425 set_prefixCFF::ParsedCStr426 void set_prefix (const Number &num, OpCode op = OpCode_Invalid) 427 { 428 has_prefix_ = true; 429 prefix_op_ = op; 430 prefix_num_ = num; 431 } 432 at_endCFF::ParsedCStr433 bool at_end (unsigned int pos) const 434 { 435 return ((pos + 1 >= values.len) /* CFF2 */ 436 || (values[pos + 1].op == OpCode_return)); 437 } 438 is_parsedCFF::ParsedCStr439 bool is_parsed () const { return parsed; } set_parsedCFF::ParsedCStr440 void set_parsed () { parsed = true; } 441 is_hint_droppedCFF::ParsedCStr442 bool is_hint_dropped () const { return hint_dropped; } set_hint_droppedCFF::ParsedCStr443 void set_hint_dropped () { hint_dropped = true; } 444 is_vsindex_droppedCFF::ParsedCStr445 bool is_vsindex_dropped () const { return vsindex_dropped; } set_vsindex_droppedCFF::ParsedCStr446 void set_vsindex_dropped () { vsindex_dropped = true; } 447 has_prefixCFF::ParsedCStr448 bool has_prefix () const { return has_prefix_; } prefix_opCFF::ParsedCStr449 OpCode prefix_op () const { return prefix_op_; } prefix_numCFF::ParsedCStr450 const Number &prefix_num () const { return prefix_num_; } 451 452 protected: 453 bool parsed; 454 bool hint_dropped; 455 bool vsindex_dropped; 456 bool has_prefix_; 457 OpCode prefix_op_; 458 Number prefix_num_; 459 460 private: 461 typedef ParsedValues<ParsedCSOp> SUPER; 462 }; 463 464 struct ParsedCStrs : hb_vector_t<ParsedCStr> 465 { initCFF::ParsedCStrs466 void init (unsigned int len_ = 0) 467 { 468 SUPER::init (); 469 resize (len_); 470 for (unsigned int i = 0; i < len; i++) 471 (*this)[i].init (); 472 } finiCFF::ParsedCStrs473 void fini () { SUPER::fini_deep (); } 474 475 private: 476 typedef hb_vector_t<ParsedCStr> SUPER; 477 }; 478 479 struct SubrSubsetParam 480 { initCFF::SubrSubsetParam481 void init (ParsedCStr *parsed_charstring_, 482 ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_, 483 hb_set_t *global_closure_, hb_set_t *local_closure_, 484 bool drop_hints_) 485 { 486 parsed_charstring = parsed_charstring_; 487 current_parsed_str = parsed_charstring; 488 parsed_global_subrs = parsed_global_subrs_; 489 parsed_local_subrs = parsed_local_subrs_; 490 global_closure = global_closure_; 491 local_closure = local_closure_; 492 drop_hints = drop_hints_; 493 } 494 get_parsed_str_for_contextCFF::SubrSubsetParam495 ParsedCStr *get_parsed_str_for_context (CallContext &context) 496 { 497 switch (context.type) 498 { 499 case CSType_CharString: 500 return parsed_charstring; 501 502 case CSType_LocalSubr: 503 if (likely (context.subr_num < parsed_local_subrs->len)) 504 return &(*parsed_local_subrs)[context.subr_num]; 505 break; 506 507 case CSType_GlobalSubr: 508 if (likely (context.subr_num < parsed_global_subrs->len)) 509 return &(*parsed_global_subrs)[context.subr_num]; 510 break; 511 } 512 return nullptr; 513 } 514 515 template <typename ENV> set_current_strCFF::SubrSubsetParam516 void set_current_str (ENV &env, bool calling) 517 { 518 ParsedCStr *parsed_str = get_parsed_str_for_context (env.context); 519 if (likely (parsed_str != nullptr)) 520 { 521 /* If the called subroutine is parsed partially but not completely yet, 522 * it must be because we are calling it recursively. 523 * Handle it as an error. */ 524 if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.len > 0))) 525 env.set_error (); 526 else 527 current_parsed_str = parsed_str; 528 } 529 else 530 env.set_error (); 531 } 532 533 ParsedCStr *current_parsed_str; 534 535 ParsedCStr *parsed_charstring; 536 ParsedCStrs *parsed_global_subrs; 537 ParsedCStrs *parsed_local_subrs; 538 hb_set_t *global_closure; 539 hb_set_t *local_closure; 540 bool drop_hints; 541 }; 542 543 struct SubrRemap : Remap 544 { createCFF::SubrRemap545 void create (hb_set_t *closure) 546 { 547 /* create a remapping of subroutine numbers from old to new. 548 * no optimization based on usage counts. fonttools doesn't appear doing that either. 549 */ 550 reset (closure->get_max () + 1); 551 for (hb_codepoint_t old_num = 0; old_num < len; old_num++) 552 { 553 if (hb_set_has (closure, old_num)) 554 add (old_num); 555 } 556 557 if (get_count () < 1240) 558 bias = 107; 559 else if (get_count () < 33900) 560 bias = 1131; 561 else 562 bias = 32768; 563 } 564 operator []CFF::SubrRemap565 hb_codepoint_t operator[] (unsigned int old_num) const 566 { 567 if (old_num >= len) 568 return CFF_UNDEF_CODE; 569 else 570 return Remap::operator[] (old_num); 571 } 572 biased_numCFF::SubrRemap573 int biased_num (unsigned int old_num) const 574 { 575 hb_codepoint_t new_num = (*this)[old_num]; 576 return (int)new_num - bias; 577 } 578 579 protected: 580 int bias; 581 }; 582 583 struct SubrRemaps 584 { SubrRemapsCFF::SubrRemaps585 SubrRemaps () 586 { 587 global_remap.init (); 588 local_remaps.init (); 589 } 590 ~SubrRemapsCFF::SubrRemaps591 ~SubrRemaps () { fini (); } 592 initCFF::SubrRemaps593 void init (unsigned int fdCount) 594 { 595 local_remaps.resize (fdCount); 596 for (unsigned int i = 0; i < fdCount; i++) 597 local_remaps[i].init (); 598 } 599 createCFF::SubrRemaps600 void create (SubrClosures& closures) 601 { 602 global_remap.create (closures.global_closure); 603 for (unsigned int i = 0; i < local_remaps.len; i++) 604 local_remaps[i].create (closures.local_closures[i]); 605 } 606 finiCFF::SubrRemaps607 void fini () 608 { 609 global_remap.fini (); 610 local_remaps.fini_deep (); 611 } 612 613 SubrRemap global_remap; 614 hb_vector_t<SubrRemap> local_remaps; 615 }; 616 617 template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET> 618 struct SubrSubsetter 619 { SubrSubsetterCFF::SubrSubsetter620 SubrSubsetter () 621 { 622 parsed_charstrings.init (); 623 parsed_global_subrs.init (); 624 parsed_local_subrs.init (); 625 } 626 ~SubrSubsetterCFF::SubrSubsetter627 ~SubrSubsetter () 628 { 629 closures.fini (); 630 remaps.fini (); 631 parsed_charstrings.fini_deep (); 632 parsed_global_subrs.fini_deep (); 633 parsed_local_subrs.fini_deep (); 634 } 635 636 /* Subroutine subsetting with --no-desubroutinize runs in phases: 637 * 638 * 1. execute charstrings/subroutines to determine subroutine closures 639 * 2. parse out all operators and numbers 640 * 3. mark hint operators and operands for removal if --no-hinting 641 * 4. re-encode all charstrings and subroutines with new subroutine numbers 642 * 643 * Phases #1 and #2 are done at the same time in collect_subrs (). 644 * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required), 645 * because we can't tell if a number belongs to a hint op until we see the first moveto. 646 * 647 * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number 648 * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. 649 */ subsetCFF::SubrSubsetter650 bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints) 651 { 652 closures.init (acc.fdCount); 653 remaps.init (acc.fdCount); 654 655 parsed_charstrings.init (glyphs.len); 656 parsed_global_subrs.init (acc.globalSubrs->count); 657 parsed_local_subrs.resize (acc.fdCount); 658 for (unsigned int i = 0; i < acc.fdCount; i++) 659 { 660 parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count); 661 } 662 if (unlikely (!closures.valid)) 663 return false; 664 665 /* phase 1 & 2 */ 666 for (unsigned int i = 0; i < glyphs.len; i++) 667 { 668 hb_codepoint_t glyph = glyphs[i]; 669 const ByteStr str = (*acc.charStrings)[glyph]; 670 unsigned int fd = acc.fdSelect->get_fd (glyph); 671 if (unlikely (fd >= acc.fdCount)) 672 return false; 673 674 CSInterpreter<ENV, OPSET, SubrSubsetParam> interp; 675 interp.env.init (str, acc, fd); 676 677 SubrSubsetParam param; 678 param.init (&parsed_charstrings[i], 679 &parsed_global_subrs, &parsed_local_subrs[fd], 680 closures.global_closure, closures.local_closures[fd], 681 drop_hints); 682 683 if (unlikely (!interp.interpret (param))) 684 return false; 685 686 /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ 687 SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]); 688 } 689 690 if (drop_hints) 691 { 692 /* mark hint ops and arguments for drop */ 693 for (unsigned int i = 0; i < glyphs.len; i++) 694 { 695 unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); 696 if (unlikely (fd >= acc.fdCount)) 697 return false; 698 SubrSubsetParam param; 699 param.init (&parsed_charstrings[i], 700 &parsed_global_subrs, &parsed_local_subrs[fd], 701 closures.global_closure, closures.local_closures[fd], 702 drop_hints); 703 704 DropHintsParam drop; 705 if (drop_hints_in_str (parsed_charstrings[i], param, drop)) 706 { 707 parsed_charstrings[i].set_hint_dropped (); 708 if (drop.vsindex_dropped) 709 parsed_charstrings[i].set_vsindex_dropped (); 710 } 711 } 712 713 /* after dropping hints recreate closures of actually used subrs */ 714 closures.reset (); 715 for (unsigned int i = 0; i < glyphs.len; i++) 716 { 717 unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); 718 if (unlikely (fd >= acc.fdCount)) 719 return false; 720 SubrSubsetParam param; 721 param.init (&parsed_charstrings[i], 722 &parsed_global_subrs, &parsed_local_subrs[fd], 723 closures.global_closure, closures.local_closures[fd], 724 drop_hints); 725 collect_subr_refs_in_str (parsed_charstrings[i], param); 726 } 727 } 728 729 remaps.create (closures); 730 731 return true; 732 } 733 encode_charstringsCFF::SubrSubsetter734 bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const 735 { 736 if (unlikely (!buffArray.resize (glyphs.len))) 737 return false; 738 for (unsigned int i = 0; i < glyphs.len; i++) 739 { 740 unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); 741 if (unlikely (fd >= acc.fdCount)) 742 return false; 743 if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) 744 return false; 745 } 746 return true; 747 } 748 encode_subrsCFF::SubrSubsetter749 bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, unsigned int fd, StrBuffArray &buffArray) const 750 { 751 unsigned int count = remap.get_count (); 752 753 if (unlikely (!buffArray.resize (count))) 754 return false; 755 for (unsigned int old_num = 0; old_num < subrs.len; old_num++) 756 { 757 hb_codepoint_t new_num = remap[old_num]; 758 if (new_num != CFF_UNDEF_CODE) 759 { 760 if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) 761 return false; 762 } 763 } 764 return true; 765 } 766 encode_globalsubrsCFF::SubrSubsetter767 bool encode_globalsubrs (StrBuffArray &buffArray) 768 { 769 return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray); 770 } 771 encode_localsubrsCFF::SubrSubsetter772 bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const 773 { 774 return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray); 775 } 776 777 protected: 778 struct DropHintsParam 779 { DropHintsParamCFF::SubrSubsetter::DropHintsParam780 DropHintsParam () 781 : seen_moveto (false), 782 ends_in_hint (false), 783 vsindex_dropped (false) {} 784 785 bool seen_moveto; 786 bool ends_in_hint; 787 bool vsindex_dropped; 788 }; 789 drop_hints_in_subrCFF::SubrSubsetter790 bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos, 791 ParsedCStrs &subrs, unsigned int subr_num, 792 const SubrSubsetParam ¶m, DropHintsParam &drop) 793 { 794 drop.ends_in_hint = false; 795 bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); 796 797 /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto), 798 * then this entire subroutine must be a hint. drop its call. */ 799 if (drop.ends_in_hint) 800 { 801 str.values[pos].set_drop (); 802 /* if this subr call is at the end of the parent subr, propagate the flag 803 * otherwise reset the flag */ 804 if (!str.at_end (pos)) 805 drop.ends_in_hint = false; 806 } 807 808 return has_hint; 809 } 810 811 /* returns true if it sees a hint op before the first moveto */ drop_hints_in_strCFF::SubrSubsetter812 bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, DropHintsParam &drop) 813 { 814 bool seen_hint = false; 815 816 for (unsigned int pos = 0; pos < str.values.len; pos++) 817 { 818 bool has_hint = false; 819 switch (str.values[pos].op) 820 { 821 case OpCode_callsubr: 822 has_hint = drop_hints_in_subr (str, pos, 823 *param.parsed_local_subrs, str.values[pos].subr_num, 824 param, drop); 825 826 break; 827 828 case OpCode_callgsubr: 829 has_hint = drop_hints_in_subr (str, pos, 830 *param.parsed_global_subrs, str.values[pos].subr_num, 831 param, drop); 832 break; 833 834 case OpCode_rmoveto: 835 case OpCode_hmoveto: 836 case OpCode_vmoveto: 837 drop.seen_moveto = true; 838 break; 839 840 case OpCode_hintmask: 841 case OpCode_cntrmask: 842 if (drop.seen_moveto) 843 { 844 str.values[pos].set_drop (); 845 break; 846 } 847 HB_FALLTHROUGH; 848 849 case OpCode_hstemhm: 850 case OpCode_vstemhm: 851 case OpCode_hstem: 852 case OpCode_vstem: 853 has_hint = true; 854 str.values[pos].set_drop (); 855 if (str.at_end (pos)) 856 drop.ends_in_hint = true; 857 break; 858 859 case OpCode_dotsection: 860 str.values[pos].set_drop (); 861 break; 862 863 default: 864 /* NONE */ 865 break; 866 } 867 if (has_hint) 868 { 869 for (int i = pos - 1; i >= 0; i--) 870 { 871 ParsedCSOp &csop = str.values[(unsigned)i]; 872 if (csop.for_drop ()) 873 break; 874 csop.set_drop (); 875 if (csop.op == OpCode_vsindexcs) 876 drop.vsindex_dropped = true; 877 } 878 seen_hint |= has_hint; 879 } 880 } 881 882 return seen_hint; 883 } 884 collect_subr_refs_in_subrCFF::SubrSubsetter885 void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos, 886 unsigned int subr_num, ParsedCStrs &subrs, 887 hb_set_t *closure, 888 const SubrSubsetParam ¶m) 889 { 890 hb_set_add (closure, subr_num); 891 collect_subr_refs_in_str (subrs[subr_num], param); 892 } 893 collect_subr_refs_in_strCFF::SubrSubsetter894 void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam ¶m) 895 { 896 for (unsigned int pos = 0; pos < str.values.len; pos++) 897 { 898 if (!str.values[pos].for_drop ()) 899 { 900 switch (str.values[pos].op) 901 { 902 case OpCode_callsubr: 903 collect_subr_refs_in_subr (str, pos, 904 str.values[pos].subr_num, *param.parsed_local_subrs, 905 param.local_closure, param); 906 break; 907 908 case OpCode_callgsubr: 909 collect_subr_refs_in_subr (str, pos, 910 str.values[pos].subr_num, *param.parsed_global_subrs, 911 param.global_closure, param); 912 break; 913 914 default: break; 915 } 916 } 917 } 918 } 919 encode_strCFF::SubrSubsetter920 bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const 921 { 922 buff.init (); 923 StrEncoder encoder (buff); 924 encoder.reset (); 925 /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, 926 * re-insert it at the beginning of charstreing */ 927 if (str.has_prefix () && str.is_hint_dropped ()) 928 { 929 encoder.encode_num (str.prefix_num ()); 930 if (str.prefix_op () != OpCode_Invalid) 931 encoder.encode_op (str.prefix_op ()); 932 } 933 for (unsigned int i = 0; i < str.get_count(); i++) 934 { 935 const ParsedCSOp &opstr = str.values[i]; 936 if (!opstr.for_drop () && !opstr.for_skip ()) 937 { 938 switch (opstr.op) 939 { 940 case OpCode_callsubr: 941 encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); 942 encoder.encode_op (OpCode_callsubr); 943 break; 944 945 case OpCode_callgsubr: 946 encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); 947 encoder.encode_op (OpCode_callgsubr); 948 break; 949 950 default: 951 encoder.copy_str (opstr.str); 952 break; 953 } 954 } 955 } 956 return !encoder.is_error (); 957 } 958 959 protected: 960 SubrClosures closures; 961 962 ParsedCStrs parsed_charstrings; 963 ParsedCStrs parsed_global_subrs; 964 hb_vector_t<ParsedCStrs> parsed_local_subrs; 965 966 SubrRemaps remaps; 967 968 private: 969 typedef typename SUBRS::count_type subr_count_type; 970 }; 971 }; /* namespace CFF */ 972 973 HB_INTERNAL bool 974 hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs, 975 unsigned int fdCount, 976 const CFF::FDSelect &src, /* IN */ 977 unsigned int &subset_fd_count /* OUT */, 978 unsigned int &subset_fdselect_size /* OUT */, 979 unsigned int &subset_fdselect_format /* OUT */, 980 hb_vector_t<CFF::code_pair> &fdselect_ranges /* OUT */, 981 CFF::Remap &fdmap /* OUT */); 982 983 HB_INTERNAL bool 984 hb_serialize_cff_fdselect (hb_serialize_context_t *c, 985 unsigned int num_glyphs, 986 const CFF::FDSelect &src, 987 unsigned int fd_count, 988 unsigned int fdselect_format, 989 unsigned int size, 990 const hb_vector_t<CFF::code_pair> &fdselect_ranges); 991 992 #endif /* HB_SUBSET_CFF_COMMON_HH */ 993