Bug 1883518 - Remove a bunch of unused ServoBindings.toml entries. r=firefox-style...
[gecko.git] / gfx / harfbuzz / src / hb-subset-cff1.cc
blobe9dd5d6427b6845c6699717ebca547852d5cb52c
1 /*
2 * Copyright © 2018 Adobe Inc.
4 * This is part of HarfBuzz, a text shaping library.
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.
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.
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.
24 * Adobe Author(s): Michiharu Ariza
27 #include "hb.hh"
29 #ifndef HB_NO_SUBSET_CFF
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-plan.hh"
36 #include "hb-subset-cff-common.hh"
37 #include "hb-cff1-interp-cs.hh"
39 using namespace CFF;
41 struct remap_sid_t
43 unsigned get_population () const { return vector.length; }
45 void alloc (unsigned size)
47 map.alloc (size);
48 vector.alloc (size, true);
51 bool in_error () const
52 { return map.in_error () || vector.in_error (); }
54 unsigned int add (unsigned int sid)
56 if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
57 return sid;
59 sid = unoffset_sid (sid);
60 unsigned v = next;
61 if (map.set (sid, v, false))
63 vector.push (sid);
64 next++;
66 else
67 v = map.get (sid); // already exists
68 return offset_sid (v);
71 unsigned int operator[] (unsigned int sid) const
73 if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
74 return sid;
76 return offset_sid (map.get (unoffset_sid (sid)));
79 static const unsigned int num_std_strings = 391;
81 static bool is_std_str (unsigned int sid) { return sid < num_std_strings; }
82 static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
83 static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
84 unsigned next = 0;
86 hb_map_t map;
87 hb_vector_t<unsigned> vector;
90 struct cff1_sub_table_info_t : cff_sub_table_info_t
92 cff1_sub_table_info_t ()
93 : cff_sub_table_info_t (),
94 encoding_link (0),
95 charset_link (0)
97 privateDictInfo.init ();
100 objidx_t encoding_link;
101 objidx_t charset_link;
102 table_info_t privateDictInfo;
105 /* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */
106 struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t
108 void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t))
110 SUPER::init ();
111 base = base_;
114 void fini () { SUPER::fini (); }
116 unsigned get_count () const { return base->get_count () + SUPER::get_count (); }
117 const cff1_top_dict_val_t &get_value (unsigned int i) const
119 if (i < base->get_count ())
120 return (*base)[i];
121 else
122 return SUPER::values[i - base->get_count ()];
124 const cff1_top_dict_val_t &operator [] (unsigned int i) const { return get_value (i); }
126 void reassignSIDs (const remap_sid_t& sidmap)
128 for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
129 nameSIDs[i] = sidmap[base->nameSIDs[i]];
132 protected:
133 typedef cff1_top_dict_values_t SUPER;
134 const cff1_top_dict_values_t *base;
137 struct top_dict_modifiers_t
139 top_dict_modifiers_t (const cff1_sub_table_info_t &info_,
140 const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount])
141 : info (info_),
142 nameSIDs (nameSIDs_)
145 const cff1_sub_table_info_t &info;
146 const unsigned int (&nameSIDs)[name_dict_values_t::ValCount];
149 struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dict_val_t>
151 bool serialize (hb_serialize_context_t *c,
152 const cff1_top_dict_val_t &opstr,
153 const top_dict_modifiers_t &mod) const
155 TRACE_SERIALIZE (this);
157 op_code_t op = opstr.op;
158 switch (op)
160 case OpCode_charset:
161 if (mod.info.charset_link)
162 return_trace (FontDict::serialize_link4_op(c, op, mod.info.charset_link, whence_t::Absolute));
163 else
164 goto fall_back;
166 case OpCode_Encoding:
167 if (mod.info.encoding_link)
168 return_trace (FontDict::serialize_link4_op(c, op, mod.info.encoding_link, whence_t::Absolute));
169 else
170 goto fall_back;
172 case OpCode_Private:
173 return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) &&
174 Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute));
176 case OpCode_version:
177 case OpCode_Notice:
178 case OpCode_Copyright:
179 case OpCode_FullName:
180 case OpCode_FamilyName:
181 case OpCode_Weight:
182 case OpCode_PostScript:
183 case OpCode_BaseFontName:
184 case OpCode_FontName:
185 return_trace (FontDict::serialize_int2_op (c, op, mod.nameSIDs[name_dict_values_t::name_op_to_index (op)]));
187 case OpCode_ROS:
189 /* for registry & ordering, reassigned SIDs are serialized
190 * for supplement, the original byte string is copied along with the op code */
191 op_str_t supp_op;
192 supp_op.op = op;
193 if ( unlikely (!(opstr.length >= opstr.last_arg_offset + 3)))
194 return_trace (false);
195 supp_op.ptr = opstr.ptr + opstr.last_arg_offset;
196 supp_op.length = opstr.length - opstr.last_arg_offset;
197 return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) &&
198 UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) &&
199 copy_opstr (c, supp_op));
201 fall_back:
202 default:
203 return_trace (cff_top_dict_op_serializer_t<cff1_top_dict_val_t>::serialize (c, opstr, mod.info));
205 return_trace (true);
210 struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t
212 bool serialize (hb_serialize_context_t *c,
213 const op_str_t &opstr,
214 const cff1_font_dict_values_mod_t &mod) const
216 TRACE_SERIALIZE (this);
218 if (opstr.op == OpCode_FontName)
219 return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName));
220 else
221 return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo));
224 private:
225 typedef cff_font_dict_op_serializer_t SUPER;
228 struct cff1_cs_opset_flatten_t : cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t>
230 static void flush_args_and_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
232 if (env.arg_start > 0)
233 flush_width (env, param);
235 switch (op)
237 case OpCode_hstem:
238 case OpCode_hstemhm:
239 case OpCode_vstem:
240 case OpCode_vstemhm:
241 case OpCode_hintmask:
242 case OpCode_cntrmask:
243 case OpCode_dotsection:
244 if (param.drop_hints)
246 env.clear_args ();
247 return;
249 HB_FALLTHROUGH;
251 default:
252 SUPER::flush_args_and_op (op, env, param);
253 break;
256 static void flush_args (cff1_cs_interp_env_t &env, flatten_param_t& param)
258 str_encoder_t encoder (param.flatStr);
259 for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
260 encoder.encode_num_cs (env.eval_arg (i));
261 SUPER::flush_args (env, param);
264 static void flush_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
266 str_encoder_t encoder (param.flatStr);
267 encoder.encode_op (op);
270 static void flush_width (cff1_cs_interp_env_t &env, flatten_param_t& param)
272 assert (env.has_width);
273 str_encoder_t encoder (param.flatStr);
274 encoder.encode_num_cs (env.width);
277 static void flush_hintmask (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
279 SUPER::flush_hintmask (op, env, param);
280 if (!param.drop_hints)
282 str_encoder_t encoder (param.flatStr);
283 for (unsigned int i = 0; i < env.hintmask_size; i++)
284 encoder.encode_byte (env.str_ref[i]);
288 private:
289 typedef cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> SUPER;
292 struct range_list_t : hb_vector_t<code_pair_t>
294 /* replace the first glyph ID in the "glyph" field each range with a nLeft value */
295 bool complete (unsigned int last_glyph)
297 hb_codepoint_t all_glyphs = 0;
298 unsigned count = this->length;
299 for (unsigned int i = count; i; i--)
301 code_pair_t &pair = arrayZ[i - 1];
302 unsigned int nLeft = last_glyph - pair.glyph - 1;
303 all_glyphs |= nLeft;
304 last_glyph = pair.glyph;
305 pair.glyph = nLeft;
307 bool two_byte = all_glyphs >= 0x100;
308 return two_byte;
312 struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t>
314 static void process_op (op_code_t op, cff1_cs_interp_env_t &env, subr_subset_param_t& param)
316 switch (op) {
318 case OpCode_return:
319 param.current_parsed_str->add_op (op, env.str_ref);
320 param.current_parsed_str->set_parsed ();
321 env.return_from_subr ();
322 param.set_current_str (env, false);
323 break;
325 case OpCode_endchar:
326 param.current_parsed_str->add_op (op, env.str_ref);
327 param.current_parsed_str->set_parsed ();
328 SUPER::process_op (op, env, param);
329 break;
331 case OpCode_callsubr:
332 process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
333 break;
335 case OpCode_callgsubr:
336 process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
337 break;
339 default:
340 SUPER::process_op (op, env, param);
341 param.current_parsed_str->add_op (op, env.str_ref);
342 break;
346 protected:
347 static void process_call_subr (op_code_t op, cs_type_t type,
348 cff1_cs_interp_env_t &env, subr_subset_param_t& param,
349 cff1_biased_subrs_t& subrs, hb_set_t *closure)
351 byte_str_ref_t str_ref = env.str_ref;
352 env.call_subr (subrs, type);
353 param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
354 closure->add (env.context.subr_num);
355 param.set_current_str (env, true);
358 private:
359 typedef cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
362 struct cff1_private_dict_op_serializer_t : op_serializer_t
364 cff1_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_)
365 : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
367 bool serialize (hb_serialize_context_t *c,
368 const op_str_t &opstr,
369 objidx_t subrs_link) const
371 TRACE_SERIALIZE (this);
373 if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
374 return_trace (true);
376 if (opstr.op == OpCode_Subrs)
378 if (desubroutinize || !subrs_link)
379 return_trace (true);
380 else
381 return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
384 return_trace (copy_opstr (c, opstr));
387 protected:
388 const bool desubroutinize;
389 const bool drop_hints;
392 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>
394 cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
395 : subr_subsetter_t (acc_, plan_) {}
397 static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
399 /* insert width at the beginning of the charstring as necessary */
400 if (env.has_width)
401 charstring.set_prefix (env.width);
403 /* subroutines/charstring left on the call stack are legally left unmarked
404 * unmarked when a subroutine terminates with endchar. mark them.
406 param.current_parsed_str->set_parsed ();
407 for (unsigned int i = 0; i < env.callStack.get_count (); i++)
409 parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]);
410 if (likely (parsed_str))
411 parsed_str->set_parsed ();
412 else
413 env.set_error ();
418 namespace OT {
419 struct cff1_subset_plan
421 cff1_subset_plan ()
423 for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
424 topDictModSIDs[i] = CFF_UNDEF_SID;
427 void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
429 const Encoding *encoding = acc.encoding;
430 unsigned int size0, size1;
431 unsigned code, last_code = CFF_UNDEF_CODE - 1;
432 hb_vector_t<hb_codepoint_t> supp_codes;
434 if (unlikely (!subset_enc_code_ranges.resize (0)))
436 plan->check_success (false);
437 return;
440 supp_codes.init ();
442 code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
443 subset_enc_num_codes = plan->num_output_glyphs () - 1;
444 unsigned int glyph;
445 auto it = hb_iter (plan->new_to_old_gid_list);
446 if (it->first == 0) it++;
447 auto _ = *it;
448 for (glyph = 1; glyph < num_glyphs; glyph++)
450 hb_codepoint_t old_glyph;
451 if (glyph == _.first)
453 old_glyph = _.second;
454 _ = *++it;
456 else
458 /* Retain the SID for the old missing glyph ID */
459 old_glyph = glyph;
461 code = acc.glyph_to_code (old_glyph, &glyph_to_sid_cache);
462 if (code == CFF_UNDEF_CODE)
464 subset_enc_num_codes = glyph - 1;
465 break;
468 if (code != last_code + 1)
469 subset_enc_code_ranges.push (code_pair_t {code, glyph});
470 last_code = code;
472 if (encoding != &Null (Encoding))
474 hb_codepoint_t sid = acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
475 encoding->get_supplement_codes (sid, supp_codes);
476 for (unsigned int i = 0; i < supp_codes.length; i++)
477 subset_enc_supp_codes.push (code_pair_t {supp_codes[i], sid});
480 supp_codes.fini ();
482 subset_enc_code_ranges.complete (glyph);
484 assert (subset_enc_num_codes <= 0xFF);
485 size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes;
486 size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.length;
488 if (size0 < size1)
489 subset_enc_format = 0;
490 else
491 subset_enc_format = 1;
494 bool plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
496 unsigned int size0, size_ranges;
497 unsigned last_sid = CFF_UNDEF_CODE - 1;
499 if (unlikely (!subset_charset_ranges.resize (0)))
501 plan->check_success (false);
502 return false;
505 code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
507 unsigned num_glyphs = plan->num_output_glyphs ();
509 if (unlikely (!subset_charset_ranges.alloc (hb_min (num_glyphs,
510 acc.num_charset_entries))))
512 plan->check_success (false);
513 return false;
516 glyph_to_sid_map_t *glyph_to_sid_map = acc.cff_accelerator ?
517 acc.cff_accelerator->glyph_to_sid_map.get_acquire () :
518 nullptr;
519 bool created_map = false;
520 if (!glyph_to_sid_map && acc.cff_accelerator)
522 created_map = true;
523 glyph_to_sid_map = acc.create_glyph_to_sid_map ();
526 auto it = hb_iter (plan->new_to_old_gid_list);
527 if (it->first == 0) it++;
528 auto _ = *it;
529 bool not_is_cid = !acc.is_CID ();
530 bool skip = !not_is_cid && glyph_to_sid_map;
531 if (not_is_cid)
532 sidmap.alloc (num_glyphs);
533 for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++)
535 hb_codepoint_t old_glyph;
536 if (glyph == _.first)
538 old_glyph = _.second;
539 _ = *++it;
541 else
543 /* Retain the SID for the old missing glyph ID */
544 old_glyph = glyph;
546 unsigned sid = glyph_to_sid_map ?
547 glyph_to_sid_map->arrayZ[old_glyph].code :
548 acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
550 if (not_is_cid)
551 sid = sidmap.add (sid);
553 if (sid != last_sid + 1)
554 subset_charset_ranges.push (code_pair_t {sid, glyph});
556 if (glyph == old_glyph && skip)
558 glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph);
559 sid += glyph - old_glyph;
561 last_sid = sid;
564 if (created_map)
566 if ((!plan->accelerator && acc.cff_accelerator) ||
567 !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
569 glyph_to_sid_map->~glyph_to_sid_map_t ();
570 hb_free (glyph_to_sid_map);
574 bool two_byte = subset_charset_ranges.complete (num_glyphs);
576 size0 = Charset0::get_size (plan->num_output_glyphs ());
577 if (!two_byte)
578 size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length);
579 else
580 size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length);
582 if (size0 < size_ranges)
583 subset_charset_format = 0;
584 else if (!two_byte)
585 subset_charset_format = 1;
586 else
587 subset_charset_format = 2;
589 return true;
592 bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
594 for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
596 unsigned int sid = acc.topDict.nameSIDs[i];
597 if (sid != CFF_UNDEF_SID)
599 topDictModSIDs[i] = sidmap.add (sid);
603 if (acc.fdArray != &Null (CFF1FDArray))
604 for (unsigned int i = 0; i < orig_fdcount; i++)
605 if (fdmap.has (i))
606 (void)sidmap.add (acc.fontDicts[i].fontName);
608 return true;
611 bool create (const OT::cff1::accelerator_subset_t &acc,
612 hb_subset_plan_t *plan)
614 /* make sure notdef is first */
615 hb_codepoint_t old_glyph;
616 if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
618 num_glyphs = plan->num_output_glyphs ();
619 orig_fdcount = acc.fdCount;
620 drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
621 desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE;
623 #ifdef HB_EXPERIMENTAL_API
624 min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
625 #else
626 min_charstrings_off_size = 0;
627 #endif
629 subset_charset = !acc.is_predef_charset ();
630 if (!subset_charset)
631 /* check whether the subset renumbers any glyph IDs */
632 for (const auto &_ : plan->new_to_old_gid_list)
634 if (_.first != _.second)
636 subset_charset = true;
637 break;
641 subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
643 /* top dict INDEX */
645 /* Add encoding/charset to a (copy of) top dict as necessary */
646 topdict_mod.init (&acc.topDict);
647 bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
648 bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
649 if (need_to_add_enc || need_to_add_set)
651 if (need_to_add_enc)
652 topdict_mod.add_op (OpCode_Encoding);
653 if (need_to_add_set)
654 topdict_mod.add_op (OpCode_charset);
658 /* Determine re-mapping of font index as fdmap among other info */
659 if (acc.fdSelect != &Null (CFF1FDSelect))
661 if (unlikely (!hb_plan_subset_cff_fdselect (plan,
662 orig_fdcount,
663 *acc.fdSelect,
664 subset_fdcount,
665 info.fd_select.size,
666 subset_fdselect_format,
667 subset_fdselect_ranges,
668 fdmap)))
669 return false;
671 else
672 fdmap.identity (1);
674 /* remove unused SIDs & reassign SIDs */
676 /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */
677 if (unlikely (!collect_sids_in_dicts (acc)))
678 return false;
679 if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
680 return false;
682 if (subset_charset && !plan_subset_charset (acc, plan))
683 return false;
685 topdict_mod.reassignSIDs (sidmap);
688 if (desubroutinize)
690 /* Flatten global & local subrs */
691 subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, OpCode_endchar>
692 flattener(acc, plan);
693 if (!flattener.flatten (subset_charstrings))
694 return false;
696 else
698 cff1_subr_subsetter_t subr_subsetter (acc, plan);
700 /* Subset subrs: collect used subroutines, leaving all unused ones behind */
701 if (!subr_subsetter.subset ())
702 return false;
704 /* encode charstrings, global subrs, local subrs with new subroutine numbers */
705 if (!subr_subsetter.encode_charstrings (subset_charstrings))
706 return false;
708 if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
709 return false;
711 /* local subrs */
712 if (!subset_localsubrs.resize (orig_fdcount))
713 return false;
714 for (unsigned int fd = 0; fd < orig_fdcount; fd++)
716 subset_localsubrs[fd].init ();
717 if (fdmap.has (fd))
719 if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
720 return false;
725 /* Encoding */
726 if (subset_encoding)
727 plan_subset_encoding (acc, plan);
729 /* private dicts & local subrs */
730 if (!acc.is_CID ())
731 fontdicts_mod.push (cff1_font_dict_values_mod_t ());
732 else
734 + hb_iter (acc.fontDicts)
735 | hb_filter ([&] (const cff1_font_dict_values_t &_)
736 { return fdmap.has (&_ - &acc.fontDicts[0]); } )
737 | hb_map ([&] (const cff1_font_dict_values_t &_)
739 cff1_font_dict_values_mod_t mod;
740 mod.init (&_, sidmap[_.fontName]);
741 return mod;
743 | hb_sink (fontdicts_mod)
747 return !plan->in_error () &&
748 (subset_charstrings.length == plan->num_output_glyphs ()) &&
749 (fontdicts_mod.length == subset_fdcount);
752 cff1_top_dict_values_mod_t topdict_mod;
753 cff1_sub_table_info_t info;
755 unsigned int num_glyphs;
756 unsigned int orig_fdcount = 0;
757 unsigned int subset_fdcount = 1;
758 unsigned int subset_fdselect_format = 0;
759 hb_vector_t<code_pair_t> subset_fdselect_ranges;
761 /* font dict index remap table from fullset FDArray to subset FDArray.
762 * set to CFF_UNDEF_CODE if excluded from subset */
763 hb_inc_bimap_t fdmap;
765 str_buff_vec_t subset_charstrings;
766 str_buff_vec_t subset_globalsubrs;
767 hb_vector_t<str_buff_vec_t> subset_localsubrs;
768 hb_vector_t<cff1_font_dict_values_mod_t> fontdicts_mod;
770 bool drop_hints = false;
772 bool gid_renum;
773 bool subset_encoding;
774 uint8_t subset_enc_format;
775 unsigned int subset_enc_num_codes;
776 range_list_t subset_enc_code_ranges;
777 hb_vector_t<code_pair_t> subset_enc_supp_codes;
779 uint8_t subset_charset_format;
780 range_list_t subset_charset_ranges;
781 bool subset_charset;
783 remap_sid_t sidmap;
784 unsigned int topDictModSIDs[name_dict_values_t::ValCount];
786 bool desubroutinize = false;
788 unsigned min_charstrings_off_size = 0;
790 } // namespace OT
792 static bool _serialize_cff1_charstrings (hb_serialize_context_t *c,
793 struct OT::cff1_subset_plan &plan,
794 const OT::cff1::accelerator_subset_t &acc)
796 c->push<CFF1CharStrings> ();
798 unsigned data_size = 0;
799 unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
800 if (unlikely (!c->start_zerocopy (total_size)))
801 return false;
803 auto *cs = c->start_embed<CFF1CharStrings> ();
804 if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) {
805 c->pop_discard ();
806 return false;
809 plan.info.char_strings_link = c->pop_pack (false);
810 return true;
813 bool
814 OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c,
815 struct OT::cff1_subset_plan &plan) const
817 /* push charstrings onto the object stack first which will ensure it packs as the last
818 object in the table. Keeping the chastrings last satisfies the requirements for patching
819 via IFTB. If this ordering needs to be changed in the future, charstrings should be left
820 at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
821 if (!_serialize_cff1_charstrings(c, plan, *this))
822 return false;
824 /* private dicts & local subrs */
825 for (int i = (int) privateDicts.length; --i >= 0 ;)
827 if (plan.fdmap.has (i))
829 objidx_t subrs_link = 0;
830 if (plan.subset_localsubrs[i].length > 0)
832 auto *dest = c->push <CFF1Subrs> ();
833 if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
834 subrs_link = c->pop_pack ();
835 else
837 c->pop_discard ();
838 return false;
842 auto *pd = c->push<PrivateDict> ();
843 cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
844 /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
845 if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
847 unsigned fd = plan.fdmap[i];
848 plan.fontdicts_mod[fd].privateDictInfo.size = c->length ();
849 plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack ();
851 else
853 c->pop_discard ();
854 return false;
859 if (!is_CID ())
860 plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
862 /* FDArray (FD Index) */
863 if (fdArray != &Null (CFF1FDArray))
865 auto *fda = c->push<CFF1FDArray> ();
866 cff1_font_dict_op_serializer_t fontSzr;
867 auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod));
868 if (likely (fda->serialize (c, it, fontSzr)))
869 plan.info.fd_array_link = c->pop_pack (false);
870 else
872 c->pop_discard ();
873 return false;
877 /* FDSelect */
878 if (fdSelect != &Null (CFF1FDSelect))
880 c->push ();
881 if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount,
882 plan.subset_fdselect_format, plan.info.fd_select.size,
883 plan.subset_fdselect_ranges)))
884 plan.info.fd_select.link = c->pop_pack ();
885 else
887 c->pop_discard ();
888 return false;
892 /* Charset */
893 if (plan.subset_charset)
895 auto *dest = c->push<Charset> ();
896 if (likely (dest->serialize (c,
897 plan.subset_charset_format,
898 plan.num_glyphs,
899 plan.subset_charset_ranges)))
900 plan.info.charset_link = c->pop_pack ();
901 else
903 c->pop_discard ();
904 return false;
908 /* Encoding */
909 if (plan.subset_encoding)
911 auto *dest = c->push<Encoding> ();
912 if (likely (dest->serialize (c,
913 plan.subset_enc_format,
914 plan.subset_enc_num_codes,
915 plan.subset_enc_code_ranges,
916 plan.subset_enc_supp_codes)))
917 plan.info.encoding_link = c->pop_pack ();
918 else
920 c->pop_discard ();
921 return false;
925 /* global subrs */
927 auto *dest = c->push <CFF1Subrs> ();
928 if (likely (dest->serialize (c, plan.subset_globalsubrs)))
929 c->pop_pack (false);
930 else
932 c->pop_discard ();
933 return false;
937 /* String INDEX */
939 auto *dest = c->push<CFF1StringIndex> ();
940 if (likely (!plan.sidmap.in_error () &&
941 dest->serialize (c, *stringIndex, plan.sidmap.vector)))
942 c->pop_pack ();
943 else
945 c->pop_discard ();
946 return false;
950 OT::cff1 *cff = c->allocate_min<OT::cff1> ();
951 if (unlikely (!cff))
952 return false;
954 /* header */
955 cff->version.major = 0x01;
956 cff->version.minor = 0x00;
957 cff->nameIndex = cff->min_size;
958 cff->offSize = 4; /* unused? */
960 /* name INDEX */
961 if (unlikely (!c->embed (*nameIndex))) return false;
963 /* top dict INDEX */
965 /* serialize singleton TopDict */
966 auto *top = c->push<TopDict> ();
967 cff1_top_dict_op_serializer_t topSzr;
968 unsigned top_size = 0;
969 top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs);
970 if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier)))
972 top_size = c->length ();
973 c->pop_pack (false);
975 else
977 c->pop_discard ();
978 return false;
980 /* serialize INDEX header for above */
981 auto *dest = c->start_embed<CFF1Index> ();
982 return dest->serialize_header (c, hb_iter (&top_size, 1), top_size);
986 bool
987 OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const
989 cff1_subset_plan cff_plan;
991 if (unlikely (!cff_plan.create (*this, c->plan)))
993 DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
994 return false;
997 return serialize (c->serializer, cff_plan);
1001 #endif