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-cff2-table.hh"
34 #include "hb-subset-plan.hh"
35 #include "hb-subset-cff-common.hh"
36 #include "hb-cff2-interp-cs.hh"
40 struct cff2_sub_table_info_t
: cff_sub_table_info_t
42 cff2_sub_table_info_t ()
43 : cff_sub_table_info_t (),
47 objidx_t var_store_link
;
50 struct cff2_top_dict_op_serializer_t
: cff_top_dict_op_serializer_t
<>
52 bool serialize (hb_serialize_context_t
*c
,
53 const op_str_t
&opstr
,
54 const cff2_sub_table_info_t
&info
) const
56 TRACE_SERIALIZE (this);
61 if (info
.var_store_link
)
62 return_trace (FontDict::serialize_link4_op(c
, opstr
.op
, info
.var_store_link
));
67 return_trace (cff_top_dict_op_serializer_t
<>::serialize (c
, opstr
, info
));
72 struct cff2_cs_opset_flatten_t
: cff2_cs_opset_t
<cff2_cs_opset_flatten_t
, flatten_param_t
, blend_arg_t
>
74 static void flush_args_and_op (op_code_t op
, cff2_cs_interp_env_t
<blend_arg_t
> &env
, flatten_param_t
& param
)
80 /* dummy opcodes in CFF2. ignore */
97 SUPER::flush_args_and_op (op
, env
, param
);
102 static void flush_args (cff2_cs_interp_env_t
<blend_arg_t
> &env
, flatten_param_t
& param
)
104 for (unsigned int i
= 0; i
< env
.argStack
.get_count ();)
106 const blend_arg_t
&arg
= env
.argStack
[i
];
109 if (unlikely (!((arg
.numValues
> 0) && (env
.argStack
.get_count () >= arg
.numValues
))))
114 flatten_blends (arg
, i
, env
, param
);
119 str_encoder_t
encoder (param
.flatStr
);
120 encoder
.encode_num_cs (arg
);
124 SUPER::flush_args (env
, param
);
127 static void flatten_blends (const blend_arg_t
&arg
, unsigned int i
, cff2_cs_interp_env_t
<blend_arg_t
> &env
, flatten_param_t
& param
)
129 /* flatten the default values */
130 str_encoder_t
encoder (param
.flatStr
);
131 for (unsigned int j
= 0; j
< arg
.numValues
; j
++)
133 const blend_arg_t
&arg1
= env
.argStack
[i
+ j
];
134 if (unlikely (!((arg1
.blending () && (arg
.numValues
== arg1
.numValues
) && (arg1
.valueIndex
== j
) &&
135 (arg1
.deltas
.length
== env
.get_region_count ())))))
140 encoder
.encode_num_cs (arg1
);
142 /* flatten deltas for each value */
143 for (unsigned int j
= 0; j
< arg
.numValues
; j
++)
145 const blend_arg_t
&arg1
= env
.argStack
[i
+ j
];
146 for (unsigned int k
= 0; k
< arg1
.deltas
.length
; k
++)
147 encoder
.encode_num_cs (arg1
.deltas
[k
]);
149 /* flatten the number of values followed by blend operator */
150 encoder
.encode_int (arg
.numValues
);
151 encoder
.encode_op (OpCode_blendcs
);
154 static void flush_op (op_code_t op
, cff2_cs_interp_env_t
<blend_arg_t
> &env
, flatten_param_t
& param
)
162 str_encoder_t
encoder (param
.flatStr
);
163 encoder
.encode_op (op
);
167 static void flush_hintmask (op_code_t op
, cff2_cs_interp_env_t
<blend_arg_t
> &env
, flatten_param_t
& param
)
169 SUPER::flush_hintmask (op
, env
, param
);
170 if (!param
.drop_hints
)
172 str_encoder_t
encoder (param
.flatStr
);
173 for (unsigned int i
= 0; i
< env
.hintmask_size
; i
++)
174 encoder
.encode_byte (env
.str_ref
[i
]);
179 typedef cff2_cs_opset_t
<cff2_cs_opset_flatten_t
, flatten_param_t
, blend_arg_t
> SUPER
;
180 typedef cs_opset_t
<blend_arg_t
, cff2_cs_opset_flatten_t
, cff2_cs_opset_flatten_t
, cff2_cs_interp_env_t
<blend_arg_t
>, flatten_param_t
> CSOPSET
;
183 struct cff2_cs_opset_subr_subset_t
: cff2_cs_opset_t
<cff2_cs_opset_subr_subset_t
, subr_subset_param_t
, blend_arg_t
>
185 static void process_op (op_code_t op
, cff2_cs_interp_env_t
<blend_arg_t
> &env
, subr_subset_param_t
& param
)
190 param
.current_parsed_str
->set_parsed ();
191 env
.return_from_subr ();
192 param
.set_current_str (env
, false);
196 param
.current_parsed_str
->set_parsed ();
197 SUPER::process_op (op
, env
, param
);
200 case OpCode_callsubr
:
201 process_call_subr (op
, CSType_LocalSubr
, env
, param
, env
.localSubrs
, param
.local_closure
);
204 case OpCode_callgsubr
:
205 process_call_subr (op
, CSType_GlobalSubr
, env
, param
, env
.globalSubrs
, param
.global_closure
);
209 SUPER::process_op (op
, env
, param
);
210 param
.current_parsed_str
->add_op (op
, env
.str_ref
);
216 static void process_call_subr (op_code_t op
, cs_type_t type
,
217 cff2_cs_interp_env_t
<blend_arg_t
> &env
, subr_subset_param_t
& param
,
218 cff2_biased_subrs_t
& subrs
, hb_set_t
*closure
)
220 byte_str_ref_t str_ref
= env
.str_ref
;
221 env
.call_subr (subrs
, type
);
222 param
.current_parsed_str
->add_call_op (op
, str_ref
, env
.context
.subr_num
);
223 closure
->add (env
.context
.subr_num
);
224 param
.set_current_str (env
, true);
228 typedef cff2_cs_opset_t
<cff2_cs_opset_subr_subset_t
, subr_subset_param_t
, blend_arg_t
> SUPER
;
231 struct cff2_subr_subsetter_t
: subr_subsetter_t
<cff2_subr_subsetter_t
, CFF2Subrs
, const OT::cff2::accelerator_subset_t
, cff2_cs_interp_env_t
<blend_arg_t
>, cff2_cs_opset_subr_subset_t
>
233 cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t
&acc_
, const hb_subset_plan_t
*plan_
)
234 : subr_subsetter_t (acc_
, plan_
) {}
236 static void complete_parsed_str (cff2_cs_interp_env_t
<blend_arg_t
> &env
, subr_subset_param_t
& param
, parsed_cs_str_t
&charstring
)
238 /* vsindex is inserted at the beginning of the charstring as necessary */
239 if (env
.seen_vsindex ())
242 ivs
.set_int ((int)env
.get_ivs ());
243 charstring
.set_prefix (ivs
, OpCode_vsindexcs
);
248 struct cff2_private_blend_encoder_param_t
250 cff2_private_blend_encoder_param_t (hb_serialize_context_t
*c
,
251 const CFF2VariationStore
*varStore
,
252 hb_array_t
<int> normalized_coords
) :
253 c (c
), varStore (varStore
), normalized_coords (normalized_coords
) {}
257 void process_blend ()
261 region_count
= varStore
->varStore
.get_region_index_count (ivs
);
262 scalars
.resize_exact (region_count
);
263 varStore
->varStore
.get_region_scalars (ivs
, normalized_coords
.arrayZ
, normalized_coords
.length
,
264 &scalars
[0], region_count
);
269 double blend_deltas (hb_array_t
<const number_t
> deltas
) const
272 if (likely (scalars
.length
== deltas
.length
))
274 unsigned count
= scalars
.length
;
275 for (unsigned i
= 0; i
< count
; i
++)
276 v
+= (double) scalars
.arrayZ
[i
] * deltas
.arrayZ
[i
].to_real ();
282 hb_serialize_context_t
*c
= nullptr;
283 bool seen_blend
= false;
285 unsigned region_count
= 0;
286 hb_vector_t
<float> scalars
;
287 const CFF2VariationStore
*varStore
= nullptr;
288 hb_array_t
<int> normalized_coords
;
291 struct cff2_private_dict_blend_opset_t
: dict_opset_t
293 static void process_arg_blend (cff2_private_blend_encoder_param_t
& param
,
295 const hb_array_t
<const number_t
> blends
,
296 unsigned n
, unsigned i
)
298 arg
.set_int (round (arg
.to_real () + param
.blend_deltas (blends
)));
301 static void process_blend (cff2_priv_dict_interp_env_t
& env
, cff2_private_blend_encoder_param_t
& param
)
305 param
.process_blend ();
306 k
= param
.region_count
;
307 n
= env
.argStack
.pop_uint ();
308 /* copy the blend values into blend array of the default values */
309 unsigned int start
= env
.argStack
.get_count () - ((k
+1) * n
);
310 /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
311 if (unlikely (start
> env
.argStack
.get_count ()))
316 for (unsigned int i
= 0; i
< n
; i
++)
318 const hb_array_t
<const number_t
> blends
= env
.argStack
.sub_array (start
+ n
+ (i
* k
), k
);
319 process_arg_blend (param
, env
.argStack
[start
+ i
], blends
, n
, i
);
322 /* pop off blend values leaving default values now adorned with blend values */
323 env
.argStack
.pop (k
* n
);
326 static void process_op (op_code_t op
, cff2_priv_dict_interp_env_t
& env
, cff2_private_blend_encoder_param_t
& param
)
331 case OpCode_BlueScale
:
332 case OpCode_BlueShift
:
333 case OpCode_BlueFuzz
:
334 case OpCode_ExpansionFactor
:
335 case OpCode_LanguageGroup
:
336 case OpCode_BlueValues
:
337 case OpCode_OtherBlues
:
338 case OpCode_FamilyBlues
:
339 case OpCode_FamilyOtherBlues
:
340 case OpCode_StemSnapH
:
341 case OpCode_StemSnapV
:
343 case OpCode_vsindexdict
:
344 env
.process_vsindex ();
345 param
.ivs
= env
.get_ivs ();
348 case OpCode_blenddict
:
349 process_blend (env
, param
);
353 dict_opset_t::process_op (op
, env
);
354 if (!env
.argStack
.is_empty ()) return;
358 if (unlikely (env
.in_error ())) return;
360 // Write args then op
363 str_encoder_t
encoder (str
);
365 unsigned count
= env
.argStack
.get_count ();
366 for (unsigned i
= 0; i
< count
; i
++)
367 encoder
.encode_num_tp (env
.argStack
[i
]);
369 encoder
.encode_op (op
);
371 auto bytes
= str
.as_bytes ();
372 param
.c
->embed (&bytes
, bytes
.length
);
378 struct cff2_private_dict_op_serializer_t
: op_serializer_t
380 cff2_private_dict_op_serializer_t (bool desubroutinize_
, bool drop_hints_
, bool pinned_
,
381 const CFF::CFF2VariationStore
* varStore_
,
382 hb_array_t
<int> normalized_coords_
)
383 : desubroutinize (desubroutinize_
), drop_hints (drop_hints_
), pinned (pinned_
),
384 varStore (varStore_
), normalized_coords (normalized_coords_
) {}
386 bool serialize (hb_serialize_context_t
*c
,
387 const op_str_t
&opstr
,
388 objidx_t subrs_link
) const
390 TRACE_SERIALIZE (this);
392 if (drop_hints
&& dict_opset_t::is_hint_op (opstr
.op
))
395 if (opstr
.op
== OpCode_Subrs
)
397 if (desubroutinize
|| !subrs_link
)
400 return_trace (FontDict::serialize_link2_op (c
, opstr
.op
, subrs_link
));
405 // Reinterpret opstr and process blends.
406 cff2_priv_dict_interp_env_t env
{hb_ubytes_t (opstr
.ptr
, opstr
.length
)};
407 cff2_private_blend_encoder_param_t
param (c
, varStore
, normalized_coords
);
408 dict_interpreter_t
<cff2_private_dict_blend_opset_t
, cff2_private_blend_encoder_param_t
, cff2_priv_dict_interp_env_t
> interp (env
);
409 return_trace (interp
.interpret (param
));
412 return_trace (copy_opstr (c
, opstr
));
416 const bool desubroutinize
;
417 const bool drop_hints
;
419 const CFF::CFF2VariationStore
* varStore
;
420 hb_array_t
<int> normalized_coords
;
425 struct cff2_subset_plan
427 bool create (const OT::cff2::accelerator_subset_t
&acc
,
428 hb_subset_plan_t
*plan
)
430 /* make sure notdef is first */
431 hb_codepoint_t old_glyph
;
432 if (!plan
->old_gid_for_new_gid (0, &old_glyph
) || (old_glyph
!= 0)) return false;
434 num_glyphs
= plan
->num_output_glyphs ();
435 orig_fdcount
= acc
.fdArray
->count
;
437 drop_hints
= plan
->flags
& HB_SUBSET_FLAGS_NO_HINTING
;
438 pinned
= (bool) plan
->normalized_coords
;
439 desubroutinize
= plan
->flags
& HB_SUBSET_FLAGS_DESUBROUTINIZE
||
440 pinned
; // For instancing we need this path
442 #ifdef HB_EXPERIMENTAL_API
443 min_charstrings_off_size
= (plan
->flags
& HB_SUBSET_FLAGS_IFTB_REQUIREMENTS
) ? 4 : 0;
445 min_charstrings_off_size
= 0;
450 /* Flatten global & local subrs */
451 subr_flattener_t
<const OT::cff2::accelerator_subset_t
, cff2_cs_interp_env_t
<blend_arg_t
>, cff2_cs_opset_flatten_t
>
452 flattener(acc
, plan
);
453 if (!flattener
.flatten (subset_charstrings
))
458 cff2_subr_subsetter_t
subr_subsetter (acc
, plan
);
460 /* Subset subrs: collect used subroutines, leaving all unused ones behind */
461 if (!subr_subsetter
.subset ())
464 /* encode charstrings, global subrs, local subrs with new subroutine numbers */
465 if (!subr_subsetter
.encode_charstrings (subset_charstrings
, !pinned
))
468 if (!subr_subsetter
.encode_globalsubrs (subset_globalsubrs
))
472 if (!subset_localsubrs
.resize (orig_fdcount
))
474 for (unsigned int fd
= 0; fd
< orig_fdcount
; fd
++)
476 subset_localsubrs
[fd
].init ();
477 if (!subr_subsetter
.encode_localsubrs (fd
, subset_localsubrs
[fd
]))
483 if (acc
.fdSelect
!= &Null (CFF2FDSelect
))
485 if (unlikely (!hb_plan_subset_cff_fdselect (plan
,
487 *(const FDSelect
*)acc
.fdSelect
,
489 subset_fdselect_size
,
490 subset_fdselect_format
,
491 subset_fdselect_ranges
,
501 cff2_sub_table_info_t info
;
503 unsigned int num_glyphs
;
504 unsigned int orig_fdcount
= 0;
505 unsigned int subset_fdcount
= 1;
506 unsigned int subset_fdselect_size
= 0;
507 unsigned int subset_fdselect_format
= 0;
509 hb_vector_t
<code_pair_t
> subset_fdselect_ranges
;
511 hb_inc_bimap_t fdmap
;
513 str_buff_vec_t subset_charstrings
;
514 str_buff_vec_t subset_globalsubrs
;
515 hb_vector_t
<str_buff_vec_t
> subset_localsubrs
;
517 bool drop_hints
= false;
518 bool desubroutinize
= false;
520 unsigned min_charstrings_off_size
= 0;
524 static bool _serialize_cff2_charstrings (hb_serialize_context_t
*c
,
525 cff2_subset_plan
&plan
,
526 const OT::cff2::accelerator_subset_t
&acc
)
530 unsigned data_size
= 0;
531 unsigned total_size
= CFF2CharStrings::total_size (plan
.subset_charstrings
, &data_size
, plan
.min_charstrings_off_size
);
532 if (unlikely (!c
->start_zerocopy (total_size
)))
535 auto *cs
= c
->start_embed
<CFF2CharStrings
> ();
536 if (unlikely (!cs
->serialize (c
, plan
.subset_charstrings
, &data_size
, plan
.min_charstrings_off_size
)))
542 plan
.info
.char_strings_link
= c
->pop_pack (false);
547 OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t
*c
,
548 struct cff2_subset_plan
&plan
,
549 hb_array_t
<int> normalized_coords
) const
551 /* push charstrings onto the object stack first which will ensure it packs as the last
552 object in the table. Keeping the chastrings last satisfies the requirements for patching
553 via IFTB. If this ordering needs to be changed in the future, charstrings should be left
554 at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
555 if (!_serialize_cff2_charstrings(c
, plan
, *this))
558 /* private dicts & local subrs */
559 hb_vector_t
<table_info_t
> private_dict_infos
;
560 if (unlikely (!private_dict_infos
.resize (plan
.subset_fdcount
))) return false;
562 for (int i
= (int)privateDicts
.length
; --i
>= 0 ;)
564 if (plan
.fdmap
.has (i
))
566 objidx_t subrs_link
= 0;
568 if (plan
.subset_localsubrs
[i
].length
> 0)
570 auto *dest
= c
->push
<CFF2Subrs
> ();
571 if (likely (dest
->serialize (c
, plan
.subset_localsubrs
[i
])))
572 subrs_link
= c
->pop_pack (false);
579 auto *pd
= c
->push
<PrivateDict
> ();
580 cff2_private_dict_op_serializer_t
privSzr (plan
.desubroutinize
, plan
.drop_hints
, plan
.pinned
,
581 varStore
, normalized_coords
);
582 if (likely (pd
->serialize (c
, privateDicts
[i
], privSzr
, subrs_link
)))
584 unsigned fd
= plan
.fdmap
[i
];
585 private_dict_infos
[fd
].size
= c
->length ();
586 private_dict_infos
[fd
].link
= c
->pop_pack ();
597 if (fdSelect
!= &Null (CFF2FDSelect
))
600 if (likely (hb_serialize_cff_fdselect (c
, plan
.num_glyphs
, *(const FDSelect
*)fdSelect
,
602 plan
.subset_fdselect_format
, plan
.subset_fdselect_size
,
603 plan
.subset_fdselect_ranges
)))
604 plan
.info
.fd_select
.link
= c
->pop_pack ();
612 /* FDArray (FD Index) */
614 auto *fda
= c
->push
<CFF2FDArray
> ();
615 cff_font_dict_op_serializer_t fontSzr
;
617 + hb_zip (+ hb_iter (fontDicts
)
618 | hb_filter ([&] (const cff2_font_dict_values_t
&_
)
619 { return plan
.fdmap
.has (&_
- &fontDicts
[0]); }),
620 hb_iter (private_dict_infos
))
622 if (unlikely (!fda
->serialize (c
, it
, fontSzr
)))
627 plan
.info
.fd_array_link
= c
->pop_pack (false);
630 /* variation store */
631 if (varStore
!= &Null (CFF2VariationStore
) &&
634 auto *dest
= c
->push
<CFF2VariationStore
> ();
635 if (unlikely (!dest
->serialize (c
, varStore
)))
640 plan
.info
.var_store_link
= c
->pop_pack (false);
643 OT::cff2
*cff2
= c
->allocate_min
<OT::cff2
> ();
644 if (unlikely (!cff2
)) return false;
647 cff2
->version
.major
= 0x02;
648 cff2
->version
.minor
= 0x00;
649 cff2
->topDict
= OT::cff2::static_size
;
653 TopDict
&dict
= cff2
+ cff2
->topDict
;
654 cff2_top_dict_op_serializer_t topSzr
;
655 if (unlikely (!dict
.serialize (c
, topDict
, topSzr
, plan
.info
))) return false;
656 cff2
->topDictSize
= c
->head
- (const char *)&dict
;
661 auto *dest
= c
->start_embed
<CFF2Subrs
> ();
662 return dest
->serialize (c
, plan
.subset_globalsubrs
);
667 OT::cff2::accelerator_subset_t::subset (hb_subset_context_t
*c
) const
669 cff2_subset_plan cff2_plan
;
671 if (unlikely (!cff2_plan
.create (*this, c
->plan
))) return false;
672 return serialize (c
->serializer
, cff2_plan
,
673 c
->plan
->normalized_coords
.as_array ());