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
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
29 #ifndef HB_NO_SUBSET_CFF
31 #include "hb-open-type.hh"
32 #include "hb-ot-cff1-table.hh"
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"
43 unsigned get_population () const { return vector
.length
; }
45 void alloc (unsigned 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
))
59 sid
= unoffset_sid (sid
);
61 if (map
.set (sid
, v
, false))
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
))
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
; }
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 (),
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
))
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 ())
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
]];
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
])
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
;
161 if (mod
.info
.charset_link
)
162 return_trace (FontDict::serialize_link4_op(c
, op
, mod
.info
.charset_link
, whence_t::Absolute
));
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
));
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
));
178 case OpCode_Copyright
:
179 case OpCode_FullName
:
180 case OpCode_FamilyName
:
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
)]));
189 /* for registry & ordering, reassigned SIDs are serialized
190 * for supplement, the original byte string is copied along with the op code */
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
));
203 return_trace (cff_top_dict_op_serializer_t
<cff1_top_dict_val_t
>::serialize (c
, opstr
, mod
.info
));
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
));
221 return_trace (SUPER::serialize (c
, opstr
, mod
.privateDictInfo
));
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
);
241 case OpCode_hintmask
:
242 case OpCode_cntrmask
:
243 case OpCode_dotsection
:
244 if (param
.drop_hints
)
252 SUPER::flush_args_and_op (op
, env
, param
);
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
]);
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;
304 last_glyph
= pair
.glyph
;
307 bool two_byte
= all_glyphs
>= 0x100;
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
)
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);
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
);
331 case OpCode_callsubr
:
332 process_call_subr (op
, CSType_LocalSubr
, env
, param
, env
.localSubrs
, param
.local_closure
);
335 case OpCode_callgsubr
:
336 process_call_subr (op
, CSType_GlobalSubr
, env
, param
, env
.globalSubrs
, param
.global_closure
);
340 SUPER::process_op (op
, env
, param
);
341 param
.current_parsed_str
->add_op (op
, env
.str_ref
);
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);
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
))
376 if (opstr
.op
== OpCode_Subrs
)
378 if (desubroutinize
|| !subrs_link
)
381 return_trace (FontDict::serialize_link2_op (c
, opstr
.op
, subrs_link
));
384 return_trace (copy_opstr (c
, opstr
));
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 */
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 ();
419 struct 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);
442 code_pair_t glyph_to_sid_cache
{0, HB_CODEPOINT_INVALID
};
443 subset_enc_num_codes
= plan
->num_output_glyphs () - 1;
445 auto it
= hb_iter (plan
->new_to_old_gid_list
);
446 if (it
->first
== 0) it
++;
448 for (glyph
= 1; glyph
< num_glyphs
; glyph
++)
450 hb_codepoint_t old_glyph
;
451 if (glyph
== _
.first
)
453 old_glyph
= _
.second
;
458 /* Retain the SID for the old missing glyph ID */
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;
468 if (code
!= last_code
+ 1)
469 subset_enc_code_ranges
.push (code_pair_t
{code
, glyph
});
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
});
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
;
489 subset_enc_format
= 0;
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);
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);
516 glyph_to_sid_map_t
*glyph_to_sid_map
= acc
.cff_accelerator
?
517 acc
.cff_accelerator
->glyph_to_sid_map
.get_acquire () :
519 bool created_map
= false;
520 if (!glyph_to_sid_map
&& acc
.cff_accelerator
)
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
++;
529 bool not_is_cid
= !acc
.is_CID ();
530 bool skip
= !not_is_cid
&& glyph_to_sid_map
;
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
;
543 /* Retain the SID for the old missing glyph ID */
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
);
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
;
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 ());
578 size_ranges
= Charset1::get_size_for_ranges (subset_charset_ranges
.length
);
580 size_ranges
= Charset2::get_size_for_ranges (subset_charset_ranges
.length
);
582 if (size0
< size_ranges
)
583 subset_charset_format
= 0;
585 subset_charset_format
= 1;
587 subset_charset_format
= 2;
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
++)
606 (void)sidmap
.add (acc
.fontDicts
[i
].fontName
);
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;
626 min_charstrings_off_size
= 0;
629 subset_charset
= !acc
.is_predef_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;
641 subset_encoding
= !acc
.is_CID() && !acc
.is_predef_encoding ();
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
)
652 topdict_mod
.add_op (OpCode_Encoding
);
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
,
666 subset_fdselect_format
,
667 subset_fdselect_ranges
,
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
)))
679 if (unlikely (sidmap
.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
682 if (subset_charset
&& !plan_subset_charset (acc
, plan
))
685 topdict_mod
.reassignSIDs (sidmap
);
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
))
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 ())
704 /* encode charstrings, global subrs, local subrs with new subroutine numbers */
705 if (!subr_subsetter
.encode_charstrings (subset_charstrings
))
708 if (!subr_subsetter
.encode_globalsubrs (subset_globalsubrs
))
712 if (!subset_localsubrs
.resize (orig_fdcount
))
714 for (unsigned int fd
= 0; fd
< orig_fdcount
; fd
++)
716 subset_localsubrs
[fd
].init ();
719 if (!subr_subsetter
.encode_localsubrs (fd
, subset_localsubrs
[fd
]))
727 plan_subset_encoding (acc
, plan
);
729 /* private dicts & local subrs */
731 fontdicts_mod
.push (cff1_font_dict_values_mod_t ());
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
]);
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;
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
;
784 unsigned int topDictModSIDs
[name_dict_values_t::ValCount
];
786 bool desubroutinize
= false;
788 unsigned min_charstrings_off_size
= 0;
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
)))
803 auto *cs
= c
->start_embed
<CFF1CharStrings
> ();
804 if (unlikely (!cs
->serialize (c
, plan
.subset_charstrings
, &data_size
, plan
.min_charstrings_off_size
))) {
809 plan
.info
.char_strings_link
= c
->pop_pack (false);
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))
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 ();
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 ();
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);
878 if (fdSelect
!= &Null (CFF1FDSelect
))
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 ();
893 if (plan
.subset_charset
)
895 auto *dest
= c
->push
<Charset
> ();
896 if (likely (dest
->serialize (c
,
897 plan
.subset_charset_format
,
899 plan
.subset_charset_ranges
)))
900 plan
.info
.charset_link
= c
->pop_pack ();
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 ();
927 auto *dest
= c
->push
<CFF1Subrs
> ();
928 if (likely (dest
->serialize (c
, plan
.subset_globalsubrs
)))
939 auto *dest
= c
->push
<CFF1StringIndex
> ();
940 if (likely (!plan
.sidmap
.in_error () &&
941 dest
->serialize (c
, *stringIndex
, plan
.sidmap
.vector
)))
950 OT::cff1
*cff
= c
->allocate_min
<OT::cff1
> ();
955 cff
->version
.major
= 0x01;
956 cff
->version
.minor
= 0x00;
957 cff
->nameIndex
= cff
->min_size
;
958 cff
->offSize
= 4; /* unused? */
961 if (unlikely (!c
->embed (*nameIndex
))) return false;
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 ();
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
);
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.");
997 return serialize (c
->serializer
, cff_plan
);