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#include "hb.hh"
28
29#ifndef HB_NO_SUBSET_CFF
30
31#include "hb-open-type.hh"
32#include "hb-ot-cff2-table.hh"
33#include "hb-set.h"
34#include "hb-subset-cff2.hh"
35#include "hb-subset-plan.hh"
36#include "hb-subset-cff-common.hh"
37#include "hb-cff2-interp-cs.hh"
38
39using namespace CFF;
40
41struct cff2_sub_table_info_t : cff_sub_table_info_t
42{
43 cff2_sub_table_info_t ()
44 : cff_sub_table_info_t (),
45 var_store_link (0)
46 {}
47
48 objidx_t var_store_link;
49};
50
51struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
52{
53 bool serialize (hb_serialize_context_t *c,
54 const op_str_t &opstr,
55 const cff2_sub_table_info_t &info) const
56 {
57 TRACE_SERIALIZE (this);
58
59 switch (opstr.op)
60 {
61 case OpCode_vstore:
62 if (info.var_store_link)
63 return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
64 else
65 return_trace (true);
66
67 default:
68 return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info));
69 }
70 }
71};
72
73struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
74{
75 static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
76 {
77 switch (op)
78 {
79 case OpCode_return:
80 case OpCode_endchar:
81 /* dummy opcodes in CFF2. ignore */
82 break;
83
84 case OpCode_hstem:
85 case OpCode_hstemhm:
86 case OpCode_vstem:
87 case OpCode_vstemhm:
88 case OpCode_hintmask:
89 case OpCode_cntrmask:
90 if (param.drop_hints)
91 {
92 env.clear_args ();
93 return;
94 }
95 HB_FALLTHROUGH;
96
97 default:
98 SUPER::flush_args_and_op (op, env, param);
99 break;
100 }
101 }
102
103 static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
104 {
105 for (unsigned int i = 0; i < env.argStack.get_count ();)
106 {
107 const blend_arg_t &arg = env.argStack[i];
108 if (arg.blending ())
109 {
110 if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
111 {
112 env.set_error ();
113 return;
114 }
115 flatten_blends (arg, i, env, param);
116 i += arg.numValues;
117 }
118 else
119 {
120 str_encoder_t encoder (param.flatStr);
121 encoder.encode_num_cs (n: arg);
122 i++;
123 }
124 }
125 SUPER::flush_args (env, param);
126 }
127
128 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 {
130 /* flatten the default values */
131 str_encoder_t encoder (param.flatStr);
132 for (unsigned int j = 0; j < arg.numValues; j++)
133 {
134 const blend_arg_t &arg1 = env.argStack[i + j];
135 if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
136 (arg1.deltas.length == env.get_region_count ())))))
137 {
138 env.set_error ();
139 return;
140 }
141 encoder.encode_num_cs (n: arg1);
142 }
143 /* flatten deltas for each value */
144 for (unsigned int j = 0; j < arg.numValues; j++)
145 {
146 const blend_arg_t &arg1 = env.argStack[i + j];
147 for (unsigned int k = 0; k < arg1.deltas.length; k++)
148 encoder.encode_num_cs (n: arg1.deltas[k]);
149 }
150 /* flatten the number of values followed by blend operator */
151 encoder.encode_int (v: arg.numValues);
152 encoder.encode_op (OpCode_blendcs);
153 }
154
155 static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
156 {
157 switch (op)
158 {
159 case OpCode_return:
160 case OpCode_endchar:
161 return;
162 default:
163 str_encoder_t encoder (param.flatStr);
164 encoder.encode_op (op);
165 }
166 }
167
168 static void flush_hintmask (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
169 {
170 SUPER::flush_hintmask (op, env, param);
171 if (!param.drop_hints)
172 {
173 str_encoder_t encoder (param.flatStr);
174 for (unsigned int i = 0; i < env.hintmask_size; i++)
175 encoder.encode_byte (b: env.str_ref[i]);
176 }
177 }
178
179 private:
180 typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
181 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;
182};
183
184struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
185{
186 static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
187 {
188 switch (op) {
189
190 case OpCode_return:
191 param.current_parsed_str->set_parsed ();
192 env.return_from_subr ();
193 param.set_current_str (env, calling: false);
194 break;
195
196 case OpCode_endchar:
197 param.current_parsed_str->set_parsed ();
198 SUPER::process_op (op, env, param);
199 break;
200
201 case OpCode_callsubr:
202 process_call_subr (op, type: CSType_LocalSubr, env, param, subrs&: env.localSubrs, closure: param.local_closure);
203 break;
204
205 case OpCode_callgsubr:
206 process_call_subr (op, type: CSType_GlobalSubr, env, param, subrs&: env.globalSubrs, closure: param.global_closure);
207 break;
208
209 default:
210 SUPER::process_op (op, env, param);
211 param.current_parsed_str->add_op (op, str_ref: env.str_ref);
212 break;
213 }
214 }
215
216 protected:
217 static void process_call_subr (op_code_t op, cs_type_t type,
218 cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
219 cff2_biased_subrs_t& subrs, hb_set_t *closure)
220 {
221 byte_str_ref_t str_ref = env.str_ref;
222 env.call_subr (biasedSubrs: subrs, type);
223 param.current_parsed_str->add_call_op (op, str_ref, subr_num: env.context.subr_num);
224 closure->add (g: env.context.subr_num);
225 param.set_current_str (env, calling: true);
226 }
227
228 private:
229 typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
230};
231
232struct 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{
234 cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
235 : subr_subsetter_t (acc_, plan_) {}
236
237 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 {
239 /* vsindex is inserted at the beginning of the charstring as necessary */
240 if (env.seen_vsindex ())
241 {
242 number_t ivs;
243 ivs.set_int ((int)env.get_ivs ());
244 charstring.set_prefix (num: ivs, OpCode_vsindexcs);
245 }
246 }
247};
248
249struct cff2_private_blend_encoder_param_t
250{
251 cff2_private_blend_encoder_param_t (hb_serialize_context_t *c,
252 const CFF2VariationStore *varStore,
253 hb_array_t<int> normalized_coords) :
254 c (c), varStore (varStore), normalized_coords (normalized_coords) {}
255
256 void init () {}
257
258 void process_blend ()
259 {
260 if (!seen_blend)
261 {
262 region_count = varStore->varStore.get_region_index_count (major: ivs);
263 scalars.resize_exact (size_: region_count);
264 varStore->varStore.get_region_scalars (major: ivs, coords: normalized_coords.arrayZ, coord_count: normalized_coords.length,
265 scalars: &scalars[0], num_scalars: region_count);
266 seen_blend = true;
267 }
268 }
269
270 double blend_deltas (hb_array_t<const number_t> deltas) const
271 {
272 double v = 0;
273 if (likely (scalars.length == deltas.length))
274 {
275 unsigned count = scalars.length;
276 for (unsigned i = 0; i < count; i++)
277 v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
278 }
279 return v;
280 }
281
282
283 hb_serialize_context_t *c = nullptr;
284 bool seen_blend = false;
285 unsigned ivs = 0;
286 unsigned region_count = 0;
287 hb_vector_t<float> scalars;
288 const CFF2VariationStore *varStore = nullptr;
289 hb_array_t<int> normalized_coords;
290};
291
292struct cff2_private_dict_blend_opset_t : dict_opset_t
293{
294 static void process_arg_blend (cff2_private_blend_encoder_param_t& param,
295 number_t &arg,
296 const hb_array_t<const number_t> blends,
297 unsigned n, unsigned i)
298 {
299 arg.set_int (round (x: arg.to_real () + param.blend_deltas (deltas: blends)));
300 }
301
302 static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
303 {
304 unsigned int n, k;
305
306 param.process_blend ();
307 k = param.region_count;
308 n = env.argStack.pop_uint ();
309 /* copy the blend values into blend array of the default values */
310 unsigned int start = env.argStack.get_count () - ((k+1) * n);
311 /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
312 if (unlikely (start > env.argStack.get_count ()))
313 {
314 env.set_error ();
315 return;
316 }
317 for (unsigned int i = 0; i < n; i++)
318 {
319 const hb_array_t<const number_t> blends = env.argStack.sub_array (start: start + n + (i * k), length: k);
320 process_arg_blend (param, arg&: env.argStack[start + i], blends, n, i);
321 }
322
323 /* pop off blend values leaving default values now adorned with blend values */
324 env.argStack.pop (n: k * n);
325 }
326
327 static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
328 {
329 switch (op) {
330 case OpCode_StdHW:
331 case OpCode_StdVW:
332 case OpCode_BlueScale:
333 case OpCode_BlueShift:
334 case OpCode_BlueFuzz:
335 case OpCode_ExpansionFactor:
336 case OpCode_LanguageGroup:
337 case OpCode_BlueValues:
338 case OpCode_OtherBlues:
339 case OpCode_FamilyBlues:
340 case OpCode_FamilyOtherBlues:
341 case OpCode_StemSnapH:
342 case OpCode_StemSnapV:
343 break;
344 case OpCode_vsindexdict:
345 env.process_vsindex ();
346 param.ivs = env.get_ivs ();
347 env.clear_args ();
348 return;
349 case OpCode_blenddict:
350 process_blend (env, param);
351 return;
352
353 default:
354 dict_opset_t::process_op (op, env);
355 if (!env.argStack.is_empty ()) return;
356 break;
357 }
358
359 if (unlikely (env.in_error ())) return;
360
361 // Write args then op
362
363 str_buff_t str;
364 str_encoder_t encoder (str);
365
366 unsigned count = env.argStack.get_count ();
367 for (unsigned i = 0; i < count; i++)
368 encoder.encode_num_tp (n: env.argStack[i]);
369
370 encoder.encode_op (op);
371
372 auto bytes = str.as_bytes ();
373 param.c->embed (obj: &bytes, size: bytes.length);
374
375 env.clear_args ();
376 }
377};
378
379struct cff2_private_dict_op_serializer_t : op_serializer_t
380{
381 cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_,
382 const CFF::CFF2VariationStore* varStore_,
383 hb_array_t<int> normalized_coords_)
384 : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_),
385 varStore (varStore_), normalized_coords (normalized_coords_) {}
386
387 bool serialize (hb_serialize_context_t *c,
388 const op_str_t &opstr,
389 objidx_t subrs_link) const
390 {
391 TRACE_SERIALIZE (this);
392
393 if (drop_hints && dict_opset_t::is_hint_op (op: opstr.op))
394 return_trace (true);
395
396 if (opstr.op == OpCode_Subrs)
397 {
398 if (desubroutinize || !subrs_link)
399 return_trace (true);
400 else
401 return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
402 }
403
404 if (pinned)
405 {
406 // Reinterpret opstr and process blends.
407 cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)};
408 cff2_private_blend_encoder_param_t param (c, varStore, normalized_coords);
409 dict_interpreter_t<cff2_private_dict_blend_opset_t, cff2_private_blend_encoder_param_t, cff2_priv_dict_interp_env_t> interp (env);
410 return_trace (interp.interpret (param));
411 }
412
413 return_trace (copy_opstr (c, opstr));
414 }
415
416 protected:
417 const bool desubroutinize;
418 const bool drop_hints;
419 const bool pinned;
420 const CFF::CFF2VariationStore* varStore;
421 hb_array_t<int> normalized_coords;
422};
423
424
425struct cff2_subset_plan
426{
427 bool create (const OT::cff2::accelerator_subset_t &acc,
428 hb_subset_plan_t *plan)
429 {
430 orig_fdcount = acc.fdArray->count;
431
432 drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
433 pinned = (bool) plan->normalized_coords;
434 desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE ||
435 pinned; // For instancing we need this path
436
437 if (desubroutinize)
438 {
439 /* Flatten global & local subrs */
440 subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
441 flattener(acc, plan);
442 if (!flattener.flatten (flat_charstrings&: subset_charstrings))
443 return false;
444 }
445 else
446 {
447 cff2_subr_subsetter_t subr_subsetter (acc, plan);
448
449 /* Subset subrs: collect used subroutines, leaving all unused ones behind */
450 if (!subr_subsetter.subset ())
451 return false;
452
453 /* encode charstrings, global subrs, local subrs with new subroutine numbers */
454 if (!subr_subsetter.encode_charstrings (buffArray&: subset_charstrings, encode_prefix: !pinned))
455 return false;
456
457 if (!subr_subsetter.encode_globalsubrs (buffArray&: subset_globalsubrs))
458 return false;
459
460 /* local subrs */
461 if (!subset_localsubrs.resize (size_: orig_fdcount))
462 return false;
463 for (unsigned int fd = 0; fd < orig_fdcount; fd++)
464 {
465 subset_localsubrs[fd].init ();
466 if (!subr_subsetter.encode_localsubrs (fd, buffArray&: subset_localsubrs[fd]))
467 return false;
468 }
469 }
470
471 /* FDSelect */
472 if (acc.fdSelect != &Null (CFF2FDSelect))
473 {
474 if (unlikely (!hb_plan_subset_cff_fdselect (plan,
475 orig_fdcount,
476 *(const FDSelect *)acc.fdSelect,
477 subset_fdcount,
478 subset_fdselect_size,
479 subset_fdselect_format,
480 subset_fdselect_ranges,
481 fdmap)))
482 return false;
483 }
484 else
485 fdmap.identity (size: 1);
486
487 return true;
488 }
489
490 cff2_sub_table_info_t info;
491
492 unsigned int orig_fdcount = 0;
493 unsigned int subset_fdcount = 1;
494 unsigned int subset_fdselect_size = 0;
495 unsigned int subset_fdselect_format = 0;
496 bool pinned = false;
497 hb_vector_t<code_pair_t> subset_fdselect_ranges;
498
499 hb_inc_bimap_t fdmap;
500
501 str_buff_vec_t subset_charstrings;
502 str_buff_vec_t subset_globalsubrs;
503 hb_vector_t<str_buff_vec_t> subset_localsubrs;
504
505 bool drop_hints = false;
506 bool desubroutinize = false;
507};
508
509static bool _serialize_cff2 (hb_serialize_context_t *c,
510 cff2_subset_plan &plan,
511 const OT::cff2::accelerator_subset_t &acc,
512 unsigned int num_glyphs,
513 hb_array_t<int> normalized_coords)
514{
515 /* private dicts & local subrs */
516 hb_vector_t<table_info_t> private_dict_infos;
517 if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
518
519 for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
520 {
521 if (plan.fdmap.has (lhs: i))
522 {
523 objidx_t subrs_link = 0;
524
525 if (plan.subset_localsubrs[i].length > 0)
526 {
527 CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
528 if (unlikely (!dest)) return false;
529 c->push ();
530 if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
531 subrs_link = c->pop_pack (share: false);
532 else
533 {
534 c->pop_discard ();
535 return false;
536 }
537 }
538 PrivateDict *pd = c->start_embed<PrivateDict> ();
539 if (unlikely (!pd)) return false;
540 c->push ();
541 cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
542 acc.varStore, normalized_coords);
543 if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
544 {
545 unsigned fd = plan.fdmap[i];
546 private_dict_infos[fd].size = c->length ();
547 private_dict_infos[fd].link = c->pop_pack ();
548 }
549 else
550 {
551 c->pop_discard ();
552 return false;
553 }
554 }
555 }
556
557 /* CharStrings */
558 {
559 c->push ();
560
561 unsigned total_size = CFF2CharStrings::total_size (iterable: plan.subset_charstrings);
562 if (unlikely (!c->start_zerocopy (total_size)))
563 return false;
564
565 CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> ();
566 if (unlikely (!cs)) return false;
567
568 if (likely (cs->serialize (c, plan.subset_charstrings)))
569 plan.info.char_strings_link = c->pop_pack (share: false);
570 else
571 {
572 c->pop_discard ();
573 return false;
574 }
575 }
576
577 /* FDSelect */
578 if (acc.fdSelect != &Null (CFF2FDSelect))
579 {
580 c->push ();
581 if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect,
582 plan.orig_fdcount,
583 plan.subset_fdselect_format, plan.subset_fdselect_size,
584 plan.subset_fdselect_ranges)))
585 plan.info.fd_select.link = c->pop_pack ();
586 else
587 {
588 c->pop_discard ();
589 return false;
590 }
591 }
592
593 /* FDArray (FD Index) */
594 {
595 c->push ();
596 CFF2FDArray *fda = c->start_embed<CFF2FDArray> ();
597 if (unlikely (!fda)) return false;
598 cff_font_dict_op_serializer_t fontSzr;
599 auto it =
600 + hb_zip (+ hb_iter (acc.fontDicts)
601 | hb_filter ([&] (const cff2_font_dict_values_t &_)
602 { return plan.fdmap.has (lhs: &_ - &acc.fontDicts[0]); }),
603 hb_iter (private_dict_infos))
604 ;
605 if (unlikely (!fda->serialize (c, it, fontSzr))) return false;
606 plan.info.fd_array_link = c->pop_pack (share: false);
607 }
608
609 /* variation store */
610 if (acc.varStore != &Null (CFF2VariationStore) &&
611 !plan.pinned)
612 {
613 c->push ();
614 CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> ();
615 if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false;
616 plan.info.var_store_link = c->pop_pack (share: false);
617 }
618
619 OT::cff2 *cff2 = c->allocate_min<OT::cff2> ();
620 if (unlikely (!cff2)) return false;
621
622 /* header */
623 cff2->version.major = 0x02;
624 cff2->version.minor = 0x00;
625 cff2->topDict = OT::cff2::static_size;
626
627 /* top dict */
628 {
629 TopDict &dict = cff2 + cff2->topDict;
630 cff2_top_dict_op_serializer_t topSzr;
631 if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false;
632 cff2->topDictSize = c->head - (const char *)&dict;
633 }
634
635 /* global subrs */
636 {
637 CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
638 if (unlikely (!dest)) return false;
639 return dest->serialize (c, iterable: plan.subset_globalsubrs);
640 }
641}
642
643static bool
644_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
645 hb_subset_context_t *c)
646{
647 cff2_subset_plan cff2_plan;
648
649 if (unlikely (!cff2_plan.create (acc, c->plan))) return false;
650 return _serialize_cff2 (c: c->serializer, plan&: cff2_plan, acc, num_glyphs: c->plan->num_output_glyphs (),
651 normalized_coords: c->plan->normalized_coords.as_array ());
652}
653
654bool
655hb_subset_cff2 (hb_subset_context_t *c)
656{
657 OT::cff2::accelerator_subset_t acc (c->plan->source);
658 return acc.is_valid () && _hb_subset_cff2 (acc, c);
659}
660
661#endif
662

source code of flutter_engine/third_party/harfbuzz/src/hb-subset-cff2.cc