Bug 1862332 [wpt PR 42877] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / gfx / harfbuzz / src / hb-subset-cff2.cc
blobabc108e5715f1812d7048398738711266c1b4108
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-cff2-table.hh"
33 #include "hb-set.h"
34 #include "hb-subset-plan.hh"
35 #include "hb-subset-cff-common.hh"
36 #include "hb-cff2-interp-cs.hh"
38 using namespace CFF;
40 struct cff2_sub_table_info_t : cff_sub_table_info_t
42 cff2_sub_table_info_t ()
43 : cff_sub_table_info_t (),
44 var_store_link (0)
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);
58 switch (opstr.op)
60 case OpCode_vstore:
61 if (info.var_store_link)
62 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
63 else
64 return_trace (true);
66 default:
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)
76 switch (op)
78 case OpCode_return:
79 case OpCode_endchar:
80 /* dummy opcodes in CFF2. ignore */
81 break;
83 case OpCode_hstem:
84 case OpCode_hstemhm:
85 case OpCode_vstem:
86 case OpCode_vstemhm:
87 case OpCode_hintmask:
88 case OpCode_cntrmask:
89 if (param.drop_hints)
91 env.clear_args ();
92 return;
94 HB_FALLTHROUGH;
96 default:
97 SUPER::flush_args_and_op (op, env, param);
98 break;
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];
107 if (arg.blending ())
109 if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
111 env.set_error ();
112 return;
114 flatten_blends (arg, i, env, param);
115 i += arg.numValues;
117 else
119 str_encoder_t encoder (param.flatStr);
120 encoder.encode_num_cs (arg);
121 i++;
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 ())))))
137 env.set_error ();
138 return;
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)
156 switch (op)
158 case OpCode_return:
159 case OpCode_endchar:
160 return;
161 default:
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]);
178 private:
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)
187 switch (op) {
189 case OpCode_return:
190 param.current_parsed_str->set_parsed ();
191 env.return_from_subr ();
192 param.set_current_str (env, false);
193 break;
195 case OpCode_endchar:
196 param.current_parsed_str->set_parsed ();
197 SUPER::process_op (op, env, param);
198 break;
200 case OpCode_callsubr:
201 process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
202 break;
204 case OpCode_callgsubr:
205 process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
206 break;
208 default:
209 SUPER::process_op (op, env, param);
210 param.current_parsed_str->add_op (op, env.str_ref);
211 break;
215 protected:
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);
227 private:
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 ())
241 number_t ivs;
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) {}
255 void init () {}
257 void process_blend ()
259 if (!seen_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);
265 seen_blend = true;
269 double blend_deltas (hb_array_t<const number_t> deltas) const
271 double v = 0;
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 ();
278 return v;
282 hb_serialize_context_t *c = nullptr;
283 bool seen_blend = false;
284 unsigned ivs = 0;
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,
294 number_t &arg,
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)
303 unsigned int n, k;
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 ()))
313 env.set_error ();
314 return;
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)
328 switch (op) {
329 case OpCode_StdHW:
330 case OpCode_StdVW:
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:
342 break;
343 case OpCode_vsindexdict:
344 env.process_vsindex ();
345 param.ivs = env.get_ivs ();
346 env.clear_args ();
347 return;
348 case OpCode_blenddict:
349 process_blend (env, param);
350 return;
352 default:
353 dict_opset_t::process_op (op, env);
354 if (!env.argStack.is_empty ()) return;
355 break;
358 if (unlikely (env.in_error ())) return;
360 // Write args then op
362 str_buff_t str;
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);
374 env.clear_args ();
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))
393 return_trace (true);
395 if (opstr.op == OpCode_Subrs)
397 if (desubroutinize || !subrs_link)
398 return_trace (true);
399 else
400 return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
403 if (pinned)
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));
415 protected:
416 const bool desubroutinize;
417 const bool drop_hints;
418 const bool pinned;
419 const CFF::CFF2VariationStore* varStore;
420 hb_array_t<int> normalized_coords;
424 namespace OT {
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;
444 #else
445 min_charstrings_off_size = 0;
446 #endif
448 if (desubroutinize)
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))
454 return false;
456 else
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 ())
462 return false;
464 /* encode charstrings, global subrs, local subrs with new subroutine numbers */
465 if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned))
466 return false;
468 if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
469 return false;
471 /* local subrs */
472 if (!subset_localsubrs.resize (orig_fdcount))
473 return false;
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]))
478 return false;
482 /* FDSelect */
483 if (acc.fdSelect != &Null (CFF2FDSelect))
485 if (unlikely (!hb_plan_subset_cff_fdselect (plan,
486 orig_fdcount,
487 *(const FDSelect *)acc.fdSelect,
488 subset_fdcount,
489 subset_fdselect_size,
490 subset_fdselect_format,
491 subset_fdselect_ranges,
492 fdmap)))
493 return false;
495 else
496 fdmap.identity (1);
498 return true;
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;
508 bool pinned = false;
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;
522 } // namespace OT
524 static bool _serialize_cff2_charstrings (hb_serialize_context_t *c,
525 cff2_subset_plan &plan,
526 const OT::cff2::accelerator_subset_t &acc)
528 c->push ();
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)))
533 return false;
535 auto *cs = c->start_embed<CFF2CharStrings> ();
536 if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size)))
538 c->pop_discard ();
539 return false;
542 plan.info.char_strings_link = c->pop_pack (false);
543 return true;
546 bool
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))
556 return false;
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);
573 else
575 c->pop_discard ();
576 return 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 ();
588 else
590 c->pop_discard ();
591 return false;
596 /* FDSelect */
597 if (fdSelect != &Null (CFF2FDSelect))
599 c->push ();
600 if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
601 plan.orig_fdcount,
602 plan.subset_fdselect_format, plan.subset_fdselect_size,
603 plan.subset_fdselect_ranges)))
604 plan.info.fd_select.link = c->pop_pack ();
605 else
607 c->pop_discard ();
608 return false;
612 /* FDArray (FD Index) */
614 auto *fda = c->push<CFF2FDArray> ();
615 cff_font_dict_op_serializer_t fontSzr;
616 auto it =
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)))
624 c->pop_discard ();
625 return false;
627 plan.info.fd_array_link = c->pop_pack (false);
630 /* variation store */
631 if (varStore != &Null (CFF2VariationStore) &&
632 !plan.pinned)
634 auto *dest = c->push<CFF2VariationStore> ();
635 if (unlikely (!dest->serialize (c, varStore)))
637 c->pop_discard ();
638 return false;
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;
646 /* header */
647 cff2->version.major = 0x02;
648 cff2->version.minor = 0x00;
649 cff2->topDict = OT::cff2::static_size;
651 /* top dict */
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;
659 /* global subrs */
661 auto *dest = c->start_embed <CFF2Subrs> ();
662 return dest->serialize (c, plan.subset_globalsubrs);
666 bool
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 ());
676 #endif