comparison mupdf-source/thirdparty/harfbuzz/src/hb-subset-cff-common.hh @ 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 #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 str_encoder_t
39 {
40 str_encoder_t (str_buff_t &buff_)
41 : buff (buff_) {}
42
43 void reset () { buff.reset (); }
44
45 void encode_byte (unsigned char b)
46 {
47 if (likely ((signed) buff.length < buff.allocated))
48 buff.arrayZ[buff.length++] = b;
49 else
50 buff.push (b);
51 }
52
53 void encode_int (int v)
54 {
55 if ((-1131 <= v) && (v <= 1131))
56 {
57 if ((-107 <= v) && (v <= 107))
58 encode_byte (v + 139);
59 else if (v > 0)
60 {
61 v -= 108;
62 encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
63 encode_byte (v & 0xFF);
64 }
65 else
66 {
67 v = -v - 108;
68 encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
69 encode_byte (v & 0xFF);
70 }
71 }
72 else
73 {
74 if (unlikely (v < -32768))
75 v = -32768;
76 else if (unlikely (v > 32767))
77 v = 32767;
78 encode_byte (OpCode_shortint);
79 encode_byte ((v >> 8) & 0xFF);
80 encode_byte (v & 0xFF);
81 }
82 }
83
84 void encode_num (const number_t& n)
85 {
86 if (n.in_int_range ())
87 {
88 encode_int (n.to_int ());
89 }
90 else
91 {
92 int32_t v = n.to_fixed ();
93 encode_byte (OpCode_fixedcs);
94 encode_byte ((v >> 24) & 0xFF);
95 encode_byte ((v >> 16) & 0xFF);
96 encode_byte ((v >> 8) & 0xFF);
97 encode_byte (v & 0xFF);
98 }
99 }
100
101 void encode_op (op_code_t op)
102 {
103 if (Is_OpCode_ESC (op))
104 {
105 encode_byte (OpCode_escape);
106 encode_byte (Unmake_OpCode_ESC (op));
107 }
108 else
109 encode_byte (op);
110 }
111
112 void copy_str (const unsigned char *str, unsigned length)
113 {
114 assert ((signed) (buff.length + length) <= buff.allocated);
115 hb_memcpy (buff.arrayZ + buff.length, str, length);
116 buff.length += length;
117 }
118
119 bool in_error () const { return buff.in_error (); }
120
121 protected:
122
123 str_buff_t &buff;
124 };
125
126 struct cff_sub_table_info_t {
127 cff_sub_table_info_t ()
128 : fd_array_link (0),
129 char_strings_link (0)
130 {
131 fd_select.init ();
132 }
133
134 table_info_t fd_select;
135 objidx_t fd_array_link;
136 objidx_t char_strings_link;
137 };
138
139 template <typename OPSTR=op_str_t>
140 struct cff_top_dict_op_serializer_t : op_serializer_t
141 {
142 bool serialize (hb_serialize_context_t *c,
143 const OPSTR &opstr,
144 const cff_sub_table_info_t &info) const
145 {
146 TRACE_SERIALIZE (this);
147
148 switch (opstr.op)
149 {
150 case OpCode_CharStrings:
151 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute));
152
153 case OpCode_FDArray:
154 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute));
155
156 case OpCode_FDSelect:
157 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute));
158
159 default:
160 return_trace (copy_opstr (c, opstr));
161 }
162 return_trace (true);
163 }
164 };
165
166 struct cff_font_dict_op_serializer_t : op_serializer_t
167 {
168 bool serialize (hb_serialize_context_t *c,
169 const op_str_t &opstr,
170 const table_info_t &privateDictInfo) const
171 {
172 TRACE_SERIALIZE (this);
173
174 if (opstr.op == OpCode_Private)
175 {
176 /* serialize the private dict size & offset as 2-byte & 4-byte integers */
177 return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) &&
178 Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute));
179 }
180 else
181 {
182 unsigned char *d = c->allocate_size<unsigned char> (opstr.length);
183 if (unlikely (!d)) return_trace (false);
184 /* Faster than hb_memcpy for small strings. */
185 for (unsigned i = 0; i < opstr.length; i++)
186 d[i] = opstr.ptr[i];
187 //hb_memcpy (d, opstr.ptr, opstr.length);
188 }
189 return_trace (true);
190 }
191 };
192
193 struct cff_private_dict_op_serializer_t : op_serializer_t
194 {
195 cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_)
196 : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
197
198 bool serialize (hb_serialize_context_t *c,
199 const op_str_t &opstr,
200 objidx_t subrs_link) const
201 {
202 TRACE_SERIALIZE (this);
203
204 if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
205 return true;
206 if (opstr.op == OpCode_Subrs)
207 {
208 if (desubroutinize || !subrs_link)
209 return_trace (true);
210 else
211 return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
212 }
213 else
214 return_trace (copy_opstr (c, opstr));
215 }
216
217 protected:
218 const bool desubroutinize;
219 const bool drop_hints;
220 };
221
222 struct flatten_param_t
223 {
224 str_buff_t &flatStr;
225 bool drop_hints;
226 };
227
228 template <typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid>
229 struct subr_flattener_t
230 {
231 subr_flattener_t (const ACC &acc_,
232 const hb_subset_plan_t *plan_)
233 : acc (acc_), plan (plan_) {}
234
235 bool flatten (str_buff_vec_t &flat_charstrings)
236 {
237 unsigned count = plan->num_output_glyphs ();
238 if (!flat_charstrings.resize (count))
239 return false;
240 for (unsigned int i = 0; i < count; i++)
241 {
242 hb_codepoint_t glyph;
243 if (!plan->old_gid_for_new_gid (i, &glyph))
244 {
245 /* add an endchar only charstring for a missing glyph if CFF1 */
246 if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op);
247 continue;
248 }
249 const hb_ubytes_t str = (*acc.charStrings)[glyph];
250 unsigned int fd = acc.fdSelect->get_fd (glyph);
251 if (unlikely (fd >= acc.fdCount))
252 return false;
253 ENV env (str, acc, fd);
254 cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env);
255 flatten_param_t param = {
256 flat_charstrings.arrayZ[i],
257 (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
258 };
259 if (unlikely (!interp.interpret (param)))
260 return false;
261 }
262 return true;
263 }
264
265 const ACC &acc;
266 const hb_subset_plan_t *plan;
267 };
268
269 struct subr_closures_t
270 {
271 subr_closures_t (unsigned int fd_count) : global_closure (), local_closures ()
272 {
273 local_closures.resize (fd_count);
274 }
275
276 void reset ()
277 {
278 global_closure.clear();
279 for (unsigned int i = 0; i < local_closures.length; i++)
280 local_closures[i].clear();
281 }
282
283 bool in_error () const { return local_closures.in_error (); }
284 hb_set_t global_closure;
285 hb_vector_t<hb_set_t> local_closures;
286 };
287
288 struct parsed_cs_op_t : op_str_t
289 {
290 parsed_cs_op_t (unsigned int subr_num_ = 0) :
291 subr_num (subr_num_) {}
292
293 bool is_hinting () const { return hinting_flag; }
294 void set_hinting () { hinting_flag = true; }
295
296 /* The layout of this struct is designed to fit within the
297 * padding of op_str_t! */
298
299 protected:
300 bool hinting_flag = false;
301
302 public:
303 uint16_t subr_num;
304 };
305
306 struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
307 {
308 parsed_cs_str_t () :
309 parsed (false),
310 hint_dropped (false),
311 has_prefix_ (false),
312 has_calls_ (false)
313 {
314 SUPER::init ();
315 }
316
317 void add_op (op_code_t op, const byte_str_ref_t& str_ref)
318 {
319 if (!is_parsed ())
320 SUPER::add_op (op, str_ref);
321 }
322
323 void add_call_op (op_code_t op, const byte_str_ref_t& str_ref, unsigned int subr_num)
324 {
325 if (!is_parsed ())
326 {
327 has_calls_ = true;
328
329 /* Pop the subroutine number. */
330 values.pop ();
331
332 SUPER::add_op (op, str_ref, {subr_num});
333 }
334 }
335
336 void set_prefix (const number_t &num, op_code_t op = OpCode_Invalid)
337 {
338 has_prefix_ = true;
339 prefix_op_ = op;
340 prefix_num_ = num;
341 }
342
343 bool at_end (unsigned int pos) const
344 {
345 return ((pos + 1 >= values.length) /* CFF2 */
346 || (values[pos + 1].op == OpCode_return));
347 }
348
349 bool is_parsed () const { return parsed; }
350 void set_parsed () { parsed = true; }
351
352 bool is_hint_dropped () const { return hint_dropped; }
353 void set_hint_dropped () { hint_dropped = true; }
354
355 bool is_vsindex_dropped () const { return vsindex_dropped; }
356 void set_vsindex_dropped () { vsindex_dropped = true; }
357
358 bool has_prefix () const { return has_prefix_; }
359 op_code_t prefix_op () const { return prefix_op_; }
360 const number_t &prefix_num () const { return prefix_num_; }
361
362 bool has_calls () const { return has_calls_; }
363
364 protected:
365 bool parsed : 1;
366 bool hint_dropped : 1;
367 bool vsindex_dropped : 1;
368 bool has_prefix_ : 1;
369 bool has_calls_ : 1;
370 op_code_t prefix_op_;
371 number_t prefix_num_;
372
373 private:
374 typedef parsed_values_t<parsed_cs_op_t> SUPER;
375 };
376
377 struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t>
378 {
379 private:
380 typedef hb_vector_t<parsed_cs_str_t> SUPER;
381 };
382
383 struct cff_subset_accelerator_t
384 {
385 static cff_subset_accelerator_t* create (
386 hb_blob_t* original_blob,
387 const parsed_cs_str_vec_t& parsed_charstrings,
388 const parsed_cs_str_vec_t& parsed_global_subrs,
389 const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) {
390 cff_subset_accelerator_t* accel =
391 (cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t));
392 new (accel) cff_subset_accelerator_t (original_blob,
393 parsed_charstrings,
394 parsed_global_subrs,
395 parsed_local_subrs);
396 return accel;
397 }
398
399 static void destroy (void* value) {
400 if (!value) return;
401
402 cff_subset_accelerator_t* accel = (cff_subset_accelerator_t*) value;
403 accel->~cff_subset_accelerator_t ();
404 hb_free (accel);
405 }
406
407 cff_subset_accelerator_t(
408 hb_blob_t* original_blob_,
409 const parsed_cs_str_vec_t& parsed_charstrings_,
410 const parsed_cs_str_vec_t& parsed_global_subrs_,
411 const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs_)
412 {
413 parsed_charstrings = parsed_charstrings_;
414 parsed_global_subrs = parsed_global_subrs_;
415 parsed_local_subrs = parsed_local_subrs_;
416
417 // the parsed charstrings point to memory in the original CFF table so we must hold a reference
418 // to it to keep the memory valid.
419 original_blob = hb_blob_reference (original_blob_);
420 }
421
422 ~cff_subset_accelerator_t() {
423 hb_blob_destroy (original_blob);
424 hb_map_destroy (glyph_to_sid_map.get_relaxed ());
425 }
426
427 parsed_cs_str_vec_t parsed_charstrings;
428 parsed_cs_str_vec_t parsed_global_subrs;
429 hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs;
430 mutable hb_atomic_ptr_t<hb_map_t> glyph_to_sid_map = nullptr;
431
432 private:
433 hb_blob_t* original_blob;
434 };
435
436 struct subr_subset_param_t
437 {
438 subr_subset_param_t (parsed_cs_str_t *parsed_charstring_,
439 parsed_cs_str_vec_t *parsed_global_subrs_,
440 parsed_cs_str_vec_t *parsed_local_subrs_,
441 hb_set_t *global_closure_,
442 hb_set_t *local_closure_,
443 bool drop_hints_) :
444 current_parsed_str (parsed_charstring_),
445 parsed_charstring (parsed_charstring_),
446 parsed_global_subrs (parsed_global_subrs_),
447 parsed_local_subrs (parsed_local_subrs_),
448 global_closure (global_closure_),
449 local_closure (local_closure_),
450 drop_hints (drop_hints_) {}
451
452 parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context)
453 {
454 switch (context.type)
455 {
456 case CSType_CharString:
457 return parsed_charstring;
458
459 case CSType_LocalSubr:
460 if (likely (context.subr_num < parsed_local_subrs->length))
461 return &(*parsed_local_subrs)[context.subr_num];
462 break;
463
464 case CSType_GlobalSubr:
465 if (likely (context.subr_num < parsed_global_subrs->length))
466 return &(*parsed_global_subrs)[context.subr_num];
467 break;
468 }
469 return nullptr;
470 }
471
472 template <typename ENV>
473 void set_current_str (ENV &env, bool calling)
474 {
475 parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context);
476 if (unlikely (!parsed_str))
477 {
478 env.set_error ();
479 return;
480 }
481 /* If the called subroutine is parsed partially but not completely yet,
482 * it must be because we are calling it recursively.
483 * Handle it as an error. */
484 if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0)))
485 env.set_error ();
486 else
487 {
488 if (!parsed_str->is_parsed ())
489 parsed_str->alloc (env.str_ref.total_size () / 2);
490 current_parsed_str = parsed_str;
491 }
492 }
493
494 parsed_cs_str_t *current_parsed_str;
495
496 parsed_cs_str_t *parsed_charstring;
497 parsed_cs_str_vec_t *parsed_global_subrs;
498 parsed_cs_str_vec_t *parsed_local_subrs;
499 hb_set_t *global_closure;
500 hb_set_t *local_closure;
501 bool drop_hints;
502 };
503
504 struct subr_remap_t : hb_inc_bimap_t
505 {
506 void create (const hb_set_t *closure)
507 {
508 /* create a remapping of subroutine numbers from old to new.
509 * no optimization based on usage counts. fonttools doesn't appear doing that either.
510 */
511
512 resize (closure->get_population ());
513 hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
514 while (hb_set_next (closure, &old_num))
515 add (old_num);
516
517 if (get_population () < 1240)
518 bias = 107;
519 else if (get_population () < 33900)
520 bias = 1131;
521 else
522 bias = 32768;
523 }
524
525 int biased_num (unsigned int old_num) const
526 {
527 hb_codepoint_t new_num = get (old_num);
528 return (int)new_num - bias;
529 }
530
531 protected:
532 int bias;
533 };
534
535 struct subr_remaps_t
536 {
537 subr_remaps_t (unsigned int fdCount)
538 {
539 local_remaps.resize (fdCount);
540 }
541
542 bool in_error()
543 {
544 return local_remaps.in_error ();
545 }
546
547 void create (subr_closures_t& closures)
548 {
549 global_remap.create (&closures.global_closure);
550 for (unsigned int i = 0; i < local_remaps.length; i++)
551 local_remaps.arrayZ[i].create (&closures.local_closures[i]);
552 }
553
554 subr_remap_t global_remap;
555 hb_vector_t<subr_remap_t> local_remaps;
556 };
557
558 template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid>
559 struct subr_subsetter_t
560 {
561 subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_)
562 : acc (acc_), plan (plan_), closures(acc_.fdCount),
563 remaps(acc_.fdCount)
564 {}
565
566 /* Subroutine subsetting with --no-desubroutinize runs in phases:
567 *
568 * 1. execute charstrings/subroutines to determine subroutine closures
569 * 2. parse out all operators and numbers
570 * 3. mark hint operators and operands for removal if --no-hinting
571 * 4. re-encode all charstrings and subroutines with new subroutine numbers
572 *
573 * Phases #1 and #2 are done at the same time in collect_subrs ().
574 * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required),
575 * because we can't tell if a number belongs to a hint op until we see the first moveto.
576 *
577 * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
578 * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
579 */
580 bool subset (void)
581 {
582 unsigned fd_count = acc.fdCount;
583 const cff_subset_accelerator_t* cff_accelerator = nullptr;
584 if (plan->accelerator && plan->accelerator->cff_accelerator) {
585 cff_accelerator = plan->accelerator->cff_accelerator;
586 fd_count = cff_accelerator->parsed_local_subrs.length;
587 }
588
589 if (cff_accelerator) {
590 // If we are not dropping hinting then charstrings are not modified so we can
591 // just use a reference to the cached copies.
592 cached_charstrings.resize (plan->num_output_glyphs ());
593 parsed_global_subrs = &cff_accelerator->parsed_global_subrs;
594 parsed_local_subrs = &cff_accelerator->parsed_local_subrs;
595 } else {
596 parsed_charstrings.resize (plan->num_output_glyphs ());
597 parsed_global_subrs_storage.resize (acc.globalSubrs->count);
598
599 if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false;
600
601 for (unsigned int i = 0; i < acc.fdCount; i++)
602 {
603 unsigned count = acc.privateDicts[i].localSubrs->count;
604 parsed_local_subrs_storage[i].resize (count);
605 if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false;
606 }
607
608 parsed_global_subrs = &parsed_global_subrs_storage;
609 parsed_local_subrs = &parsed_local_subrs_storage;
610 }
611
612 if (unlikely (remaps.in_error()
613 || cached_charstrings.in_error ()
614 || parsed_charstrings.in_error ()
615 || parsed_global_subrs->in_error ()
616 || closures.in_error ())) {
617 return false;
618 }
619
620 /* phase 1 & 2 */
621 for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
622 {
623 hb_codepoint_t glyph;
624 if (!plan->old_gid_for_new_gid (i, &glyph))
625 continue;
626
627 const hb_ubytes_t str = (*acc.charStrings)[glyph];
628 unsigned int fd = acc.fdSelect->get_fd (glyph);
629 if (unlikely (fd >= acc.fdCount))
630 return false;
631
632 if (cff_accelerator)
633 {
634 // parsed string already exists in accelerator, copy it and move
635 // on.
636 if (cached_charstrings)
637 cached_charstrings[i] = &cff_accelerator->parsed_charstrings[glyph];
638 else
639 parsed_charstrings[i] = cff_accelerator->parsed_charstrings[glyph];
640
641 continue;
642 }
643
644 ENV env (str, acc, fd);
645 cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env);
646
647 parsed_charstrings[i].alloc (str.length / 2);
648 subr_subset_param_t param (&parsed_charstrings[i],
649 &parsed_global_subrs_storage,
650 &parsed_local_subrs_storage[fd],
651 &closures.global_closure,
652 &closures.local_closures[fd],
653 plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
654
655 if (unlikely (!interp.interpret (param)))
656 return false;
657
658 /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
659 SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]);
660 }
661
662 // Since parsed strings were loaded from accelerator, we still need
663 // to compute the subroutine closures which would have normally happened during
664 // parsing.
665 if (cff_accelerator &&
666 !closure_subroutines(*parsed_global_subrs,
667 *parsed_local_subrs))
668 return false;
669
670 if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING && !cff_accelerator) ||
671 plan->inprogress_accelerator)
672 {
673 /* mark hint ops and arguments for drop */
674 for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
675 {
676 hb_codepoint_t glyph;
677 if (!plan->old_gid_for_new_gid (i, &glyph))
678 continue;
679 unsigned int fd = acc.fdSelect->get_fd (glyph);
680 if (unlikely (fd >= acc.fdCount))
681 return false;
682 subr_subset_param_t param (&parsed_charstrings[i],
683 &parsed_global_subrs_storage,
684 &parsed_local_subrs_storage[fd],
685 &closures.global_closure,
686 &closures.local_closures[fd],
687 plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
688
689 drop_hints_param_t drop;
690 if (drop_hints_in_str (parsed_charstrings[i], param, drop))
691 {
692 parsed_charstrings[i].set_hint_dropped ();
693 if (drop.vsindex_dropped)
694 parsed_charstrings[i].set_vsindex_dropped ();
695 }
696 }
697
698 /* after dropping hints recreate closures of actually used subrs */
699 if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING &&
700 !cff_accelerator &&
701 !closure_subroutines(*parsed_global_subrs, *parsed_local_subrs)) return false;
702 }
703
704 remaps.create (closures);
705
706 populate_subset_accelerator ();
707 return true;
708 }
709
710 bool encode_charstrings (str_buff_vec_t &buffArray) const
711 {
712 if (unlikely (!buffArray.resize (plan->num_output_glyphs ())))
713 return false;
714 for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
715 {
716 hb_codepoint_t glyph;
717 if (!plan->old_gid_for_new_gid (i, &glyph))
718 {
719 /* add an endchar only charstring for a missing glyph if CFF1 */
720 if (endchar_op != OpCode_Invalid) buffArray.arrayZ[i].push (endchar_op);
721 continue;
722 }
723 unsigned int fd = acc.fdSelect->get_fd (glyph);
724 if (unlikely (fd >= acc.fdCount))
725 return false;
726 if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i])))
727 return false;
728 }
729 return true;
730 }
731
732 bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const
733 {
734 unsigned int count = remap.get_population ();
735
736 if (unlikely (!buffArray.resize (count)))
737 return false;
738 for (unsigned int new_num = 0; new_num < count; new_num++)
739 {
740 hb_codepoint_t old_num = remap.backward (new_num);
741 assert (old_num != CFF_UNDEF_CODE);
742
743 if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num])))
744 return false;
745 }
746 return true;
747 }
748
749 bool encode_globalsubrs (str_buff_vec_t &buffArray)
750 {
751 return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray);
752 }
753
754 bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const
755 {
756 return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray);
757 }
758
759 protected:
760 struct drop_hints_param_t
761 {
762 drop_hints_param_t ()
763 : seen_moveto (false),
764 ends_in_hint (false),
765 all_dropped (false),
766 vsindex_dropped (false) {}
767
768 bool seen_moveto;
769 bool ends_in_hint;
770 bool all_dropped;
771 bool vsindex_dropped;
772 };
773
774 bool drop_hints_in_subr (parsed_cs_str_t &str, unsigned int pos,
775 parsed_cs_str_vec_t &subrs, unsigned int subr_num,
776 const subr_subset_param_t &param, drop_hints_param_t &drop)
777 {
778 drop.ends_in_hint = false;
779 bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
780
781 /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto),
782 * then this entire subroutine must be a hint. drop its call. */
783 if (drop.ends_in_hint)
784 {
785 str.values[pos].set_hinting ();
786 /* if this subr call is at the end of the parent subr, propagate the flag
787 * otherwise reset the flag */
788 if (!str.at_end (pos))
789 drop.ends_in_hint = false;
790 }
791 else if (drop.all_dropped)
792 {
793 str.values[pos].set_hinting ();
794 }
795
796 return has_hint;
797 }
798
799 /* returns true if it sees a hint op before the first moveto */
800 bool drop_hints_in_str (parsed_cs_str_t &str, const subr_subset_param_t &param, drop_hints_param_t &drop)
801 {
802 bool seen_hint = false;
803
804 unsigned count = str.values.length;
805 auto *values = str.values.arrayZ;
806 for (unsigned int pos = 0; pos < count; pos++)
807 {
808 bool has_hint = false;
809 switch (values[pos].op)
810 {
811 case OpCode_callsubr:
812 has_hint = drop_hints_in_subr (str, pos,
813 *param.parsed_local_subrs, values[pos].subr_num,
814 param, drop);
815 break;
816
817 case OpCode_callgsubr:
818 has_hint = drop_hints_in_subr (str, pos,
819 *param.parsed_global_subrs, values[pos].subr_num,
820 param, drop);
821 break;
822
823 case OpCode_rmoveto:
824 case OpCode_hmoveto:
825 case OpCode_vmoveto:
826 drop.seen_moveto = true;
827 break;
828
829 case OpCode_hintmask:
830 case OpCode_cntrmask:
831 if (drop.seen_moveto)
832 {
833 values[pos].set_hinting ();
834 break;
835 }
836 HB_FALLTHROUGH;
837
838 case OpCode_hstemhm:
839 case OpCode_vstemhm:
840 case OpCode_hstem:
841 case OpCode_vstem:
842 has_hint = true;
843 values[pos].set_hinting ();
844 if (str.at_end (pos))
845 drop.ends_in_hint = true;
846 break;
847
848 case OpCode_dotsection:
849 values[pos].set_hinting ();
850 break;
851
852 default:
853 /* NONE */
854 break;
855 }
856 if (has_hint)
857 {
858 for (int i = pos - 1; i >= 0; i--)
859 {
860 parsed_cs_op_t &csop = values[(unsigned)i];
861 if (csop.is_hinting ())
862 break;
863 csop.set_hinting ();
864 if (csop.op == OpCode_vsindexcs)
865 drop.vsindex_dropped = true;
866 }
867 seen_hint |= has_hint;
868 }
869 }
870
871 /* Raise all_dropped flag if all operators except return are dropped from a subr.
872 * It may happen even after seeing the first moveto if a subr contains
873 * only (usually one) hintmask operator, then calls to this subr can be dropped.
874 */
875 drop.all_dropped = true;
876 for (unsigned int pos = 0; pos < count; pos++)
877 {
878 parsed_cs_op_t &csop = values[pos];
879 if (csop.op == OpCode_return)
880 break;
881 if (!csop.is_hinting ())
882 {
883 drop.all_dropped = false;
884 break;
885 }
886 }
887
888 return seen_hint;
889 }
890
891 bool closure_subroutines (const parsed_cs_str_vec_t& global_subrs,
892 const hb_vector_t<parsed_cs_str_vec_t>& local_subrs)
893 {
894 closures.reset ();
895 for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
896 {
897 hb_codepoint_t glyph;
898 if (!plan->old_gid_for_new_gid (i, &glyph))
899 continue;
900 unsigned int fd = acc.fdSelect->get_fd (glyph);
901 if (unlikely (fd >= acc.fdCount))
902 return false;
903
904 // Note: const cast is safe here because the collect_subr_refs_in_str only performs a
905 // closure and does not modify any of the charstrings.
906 subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (i)),
907 const_cast<parsed_cs_str_vec_t*> (&global_subrs),
908 const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]),
909 &closures.global_closure,
910 &closures.local_closures[fd],
911 plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
912 collect_subr_refs_in_str (get_parsed_charstring (i), param);
913 }
914
915 return true;
916 }
917
918 void collect_subr_refs_in_subr (unsigned int subr_num, parsed_cs_str_vec_t &subrs,
919 hb_set_t *closure,
920 const subr_subset_param_t &param)
921 {
922 if (closure->has (subr_num))
923 return;
924 closure->add (subr_num);
925 collect_subr_refs_in_str (subrs[subr_num], param);
926 }
927
928 void collect_subr_refs_in_str (const parsed_cs_str_t &str,
929 const subr_subset_param_t &param)
930 {
931 if (!str.has_calls ())
932 return;
933
934 for (auto &opstr : str.values)
935 {
936 if (!param.drop_hints || !opstr.is_hinting ())
937 {
938 switch (opstr.op)
939 {
940 case OpCode_callsubr:
941 collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs,
942 param.local_closure, param);
943 break;
944
945 case OpCode_callgsubr:
946 collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs,
947 param.global_closure, param);
948 break;
949
950 default: break;
951 }
952 }
953 }
954 }
955
956 bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const
957 {
958 str_encoder_t encoder (buff);
959 encoder.reset ();
960 bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
961 /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
962 * re-insert it at the beginning of charstreing */
963 if (str.has_prefix () && !hinting && str.is_hint_dropped ())
964 {
965 encoder.encode_num (str.prefix_num ());
966 if (str.prefix_op () != OpCode_Invalid)
967 encoder.encode_op (str.prefix_op ());
968 }
969
970 unsigned size = 0;
971 for (auto &opstr : str.values)
972 {
973 size += opstr.length;
974 if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr)
975 size += 3;
976 }
977 if (!buff.alloc (buff.length + size))
978 return false;
979
980 for (auto &opstr : str.values)
981 {
982 if (hinting || !opstr.is_hinting ())
983 {
984 switch (opstr.op)
985 {
986 case OpCode_callsubr:
987 encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
988 encoder.copy_str (opstr.ptr, opstr.length);
989 break;
990
991 case OpCode_callgsubr:
992 encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
993 encoder.copy_str (opstr.ptr, opstr.length);
994 break;
995
996 default:
997 encoder.copy_str (opstr.ptr, opstr.length);
998 break;
999 }
1000 }
1001 }
1002 return !encoder.in_error ();
1003 }
1004
1005 void compact_parsed_strings () const
1006 {
1007 for (auto &cs : parsed_charstrings)
1008 compact_string (cs);
1009 for (auto &cs : parsed_global_subrs_storage)
1010 compact_string (cs);
1011 for (auto &vec : parsed_local_subrs_storage)
1012 for (auto &cs : vec)
1013 compact_string (cs);
1014 }
1015
1016 static void compact_string (parsed_cs_str_t &str)
1017 {
1018 unsigned count = str.values.length;
1019 if (unlikely (!count)) return;
1020 auto &opstr = str.values.arrayZ;
1021 unsigned j = 0;
1022 for (unsigned i = 1; i < count; i++)
1023 {
1024 /* See if we can combine op j and op i. */
1025 bool combine =
1026 (opstr[j].op != OpCode_callsubr && opstr[j].op != OpCode_callgsubr) &&
1027 (opstr[i].op != OpCode_callsubr && opstr[i].op != OpCode_callgsubr) &&
1028 (opstr[j].is_hinting () == opstr[i].is_hinting ()) &&
1029 (opstr[j].ptr + opstr[j].length == opstr[i].ptr) &&
1030 (opstr[j].length + opstr[i].length <= 255);
1031
1032 if (combine)
1033 {
1034 opstr[j].length += opstr[i].length;
1035 opstr[j].op = OpCode_Invalid;
1036 }
1037 else
1038 {
1039 opstr[++j] = opstr[i];
1040 }
1041 }
1042 str.values.shrink (j + 1);
1043 }
1044
1045 void populate_subset_accelerator () const
1046 {
1047 if (!plan->inprogress_accelerator) return;
1048
1049 compact_parsed_strings ();
1050
1051 plan->inprogress_accelerator->cff_accelerator =
1052 cff_subset_accelerator_t::create(acc.blob,
1053 parsed_charstrings,
1054 parsed_global_subrs_storage,
1055 parsed_local_subrs_storage);
1056 plan->inprogress_accelerator->destroy_cff_accelerator =
1057 cff_subset_accelerator_t::destroy;
1058
1059 }
1060
1061 const parsed_cs_str_t& get_parsed_charstring (unsigned i) const
1062 {
1063 if (cached_charstrings) return *(cached_charstrings[i]);
1064 return parsed_charstrings[i];
1065 }
1066
1067 protected:
1068 const ACC &acc;
1069 const hb_subset_plan_t *plan;
1070
1071 subr_closures_t closures;
1072
1073 hb_vector_t<const parsed_cs_str_t*> cached_charstrings;
1074 const parsed_cs_str_vec_t* parsed_global_subrs;
1075 const hb_vector_t<parsed_cs_str_vec_t>* parsed_local_subrs;
1076
1077 subr_remaps_t remaps;
1078
1079 private:
1080
1081 parsed_cs_str_vec_t parsed_charstrings;
1082 parsed_cs_str_vec_t parsed_global_subrs_storage;
1083 hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs_storage;
1084 typedef typename SUBRS::count_type subr_count_type;
1085 };
1086
1087 } /* namespace CFF */
1088
1089 HB_INTERNAL bool
1090 hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan,
1091 unsigned int fdCount,
1092 const CFF::FDSelect &src, /* IN */
1093 unsigned int &subset_fd_count /* OUT */,
1094 unsigned int &subset_fdselect_size /* OUT */,
1095 unsigned int &subset_fdselect_format /* OUT */,
1096 hb_vector_t<CFF::code_pair_t> &fdselect_ranges /* OUT */,
1097 hb_inc_bimap_t &fdmap /* OUT */);
1098
1099 HB_INTERNAL bool
1100 hb_serialize_cff_fdselect (hb_serialize_context_t *c,
1101 unsigned int num_glyphs,
1102 const CFF::FDSelect &src,
1103 unsigned int fd_count,
1104 unsigned int fdselect_format,
1105 unsigned int size,
1106 const hb_vector_t<CFF::code_pair_t> &fdselect_ranges);
1107
1108 #endif /* HB_SUBSET_CFF_COMMON_HH */