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 | |
39 | using namespace CFF; |
40 | |
41 | struct 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 | |
51 | struct 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 | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); |
63 | |
64 | default: |
65 | return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); |
66 | } |
67 | } |
68 | }; |
69 | |
70 | struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> |
71 | { |
72 | static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) |
73 | { |
74 | switch (op) |
75 | { |
76 | case OpCode_return: |
77 | case OpCode_endchar: |
78 | /* dummy opcodes in CFF2. ignore */ |
79 | break; |
80 | |
81 | case OpCode_hstem: |
82 | case OpCode_hstemhm: |
83 | case OpCode_vstem: |
84 | case OpCode_vstemhm: |
85 | case OpCode_hintmask: |
86 | case OpCode_cntrmask: |
87 | if (param.drop_hints) |
88 | { |
89 | env.clear_args (); |
90 | return; |
91 | } |
92 | HB_FALLTHROUGH; |
93 | |
94 | default: |
95 | SUPER::flush_args_and_op (op, env, param); |
96 | break; |
97 | } |
98 | } |
99 | |
100 | static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param) |
101 | { |
102 | for (unsigned int i = 0; i < env.argStack.get_count ();) |
103 | { |
104 | const blend_arg_t &arg = env.argStack[i]; |
105 | if (arg.blending ()) |
106 | { |
107 | if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues)))) |
108 | { |
109 | env.set_error (); |
110 | return; |
111 | } |
112 | flatten_blends (arg, i, env, param); |
113 | i += arg.numValues; |
114 | } |
115 | else |
116 | { |
117 | str_encoder_t encoder (param.flatStr); |
118 | encoder.encode_num (arg); |
119 | i++; |
120 | } |
121 | } |
122 | SUPER::flush_args (env, param); |
123 | } |
124 | |
125 | static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param) |
126 | { |
127 | /* flatten the default values */ |
128 | str_encoder_t encoder (param.flatStr); |
129 | for (unsigned int j = 0; j < arg.numValues; j++) |
130 | { |
131 | const blend_arg_t &arg1 = env.argStack[i + j]; |
132 | if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && |
133 | (arg1.deltas.length == env.get_region_count ()))))) |
134 | { |
135 | env.set_error (); |
136 | return; |
137 | } |
138 | encoder.encode_num (arg1); |
139 | } |
140 | /* flatten deltas for each value */ |
141 | for (unsigned int j = 0; j < arg.numValues; j++) |
142 | { |
143 | const blend_arg_t &arg1 = env.argStack[i + j]; |
144 | for (unsigned int k = 0; k < arg1.deltas.length; k++) |
145 | encoder.encode_num (arg1.deltas[k]); |
146 | } |
147 | /* flatten the number of values followed by blend operator */ |
148 | encoder.encode_int (arg.numValues); |
149 | encoder.encode_op (OpCode_blendcs); |
150 | } |
151 | |
152 | static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) |
153 | { |
154 | switch (op) |
155 | { |
156 | case OpCode_return: |
157 | case OpCode_endchar: |
158 | return; |
159 | default: |
160 | str_encoder_t encoder (param.flatStr); |
161 | encoder.encode_op (op); |
162 | } |
163 | } |
164 | |
165 | private: |
166 | typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER; |
167 | typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET; |
168 | }; |
169 | |
170 | struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> |
171 | { |
172 | static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param) |
173 | { |
174 | switch (op) { |
175 | |
176 | case OpCode_return: |
177 | param.current_parsed_str->set_parsed (); |
178 | env.return_from_subr (); |
179 | param.set_current_str (env, false); |
180 | break; |
181 | |
182 | case OpCode_endchar: |
183 | param.current_parsed_str->set_parsed (); |
184 | SUPER::process_op (op, env, param); |
185 | break; |
186 | |
187 | case OpCode_callsubr: |
188 | process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); |
189 | break; |
190 | |
191 | case OpCode_callgsubr: |
192 | process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); |
193 | break; |
194 | |
195 | default: |
196 | SUPER::process_op (op, env, param); |
197 | param.current_parsed_str->add_op (op, env.str_ref); |
198 | break; |
199 | } |
200 | } |
201 | |
202 | protected: |
203 | static void process_call_subr (op_code_t op, cs_type_t type, |
204 | cff2_cs_interp_env_t &env, subr_subset_param_t& param, |
205 | cff2_biased_subrs_t& subrs, hb_set_t *closure) |
206 | { |
207 | byte_str_ref_t str_ref = env.str_ref; |
208 | env.call_subr (subrs, type); |
209 | param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); |
210 | closure->add (env.context.subr_num); |
211 | param.set_current_str (env, true); |
212 | } |
213 | |
214 | private: |
215 | typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER; |
216 | }; |
217 | |
218 | struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t> |
219 | { |
220 | cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) |
221 | : subr_subsetter_t (acc_, plan_) {} |
222 | |
223 | static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) |
224 | { |
225 | /* vsindex is inserted at the beginning of the charstring as necessary */ |
226 | if (env.seen_vsindex ()) |
227 | { |
228 | number_t ivs; |
229 | ivs.set_int ((int)env.get_ivs ()); |
230 | charstring.set_prefix (ivs, OpCode_vsindexcs); |
231 | } |
232 | } |
233 | }; |
234 | |
235 | struct cff2_subset_plan { |
236 | cff2_subset_plan () |
237 | : orig_fdcount (0), |
238 | subset_fdcount(1), |
239 | subset_fdselect_size (0), |
240 | subset_fdselect_format (0), |
241 | drop_hints (false), |
242 | desubroutinize (false) |
243 | { |
244 | subset_fdselect_ranges.init (); |
245 | fdmap.init (); |
246 | subset_charstrings.init (); |
247 | subset_globalsubrs.init (); |
248 | subset_localsubrs.init (); |
249 | } |
250 | |
251 | ~cff2_subset_plan () |
252 | { |
253 | subset_fdselect_ranges.fini (); |
254 | fdmap.fini (); |
255 | subset_charstrings.fini_deep (); |
256 | subset_globalsubrs.fini_deep (); |
257 | subset_localsubrs.fini_deep (); |
258 | } |
259 | |
260 | bool create (const OT::cff2::accelerator_subset_t &acc, |
261 | hb_subset_plan_t *plan) |
262 | { |
263 | orig_fdcount = acc.fdArray->count; |
264 | |
265 | drop_hints = plan->drop_hints; |
266 | desubroutinize = plan->desubroutinize; |
267 | |
268 | if (desubroutinize) |
269 | { |
270 | /* Flatten global & local subrs */ |
271 | subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t> |
272 | flattener(acc, plan); |
273 | if (!flattener.flatten (subset_charstrings)) |
274 | return false; |
275 | } |
276 | else |
277 | { |
278 | cff2_subr_subsetter_t subr_subsetter (acc, plan); |
279 | |
280 | /* Subset subrs: collect used subroutines, leaving all unused ones behind */ |
281 | if (!subr_subsetter.subset ()) |
282 | return false; |
283 | |
284 | /* encode charstrings, global subrs, local subrs with new subroutine numbers */ |
285 | if (!subr_subsetter.encode_charstrings (subset_charstrings)) |
286 | return false; |
287 | |
288 | if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) |
289 | return false; |
290 | |
291 | /* local subrs */ |
292 | if (!subset_localsubrs.resize (orig_fdcount)) |
293 | return false; |
294 | for (unsigned int fd = 0; fd < orig_fdcount; fd++) |
295 | { |
296 | subset_localsubrs[fd].init (); |
297 | if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) |
298 | return false; |
299 | } |
300 | } |
301 | |
302 | /* FDSelect */ |
303 | if (acc.fdSelect != &Null (CFF2FDSelect)) |
304 | { |
305 | if (unlikely (!hb_plan_subset_cff_fdselect (plan, |
306 | orig_fdcount, |
307 | *(const FDSelect *)acc.fdSelect, |
308 | subset_fdcount, |
309 | subset_fdselect_size, |
310 | subset_fdselect_format, |
311 | subset_fdselect_ranges, |
312 | fdmap))) |
313 | return false; |
314 | } |
315 | else |
316 | fdmap.identity (1); |
317 | |
318 | return true; |
319 | } |
320 | |
321 | cff2_sub_table_info_t info; |
322 | |
323 | unsigned int orig_fdcount; |
324 | unsigned int subset_fdcount; |
325 | unsigned int subset_fdselect_size; |
326 | unsigned int subset_fdselect_format; |
327 | hb_vector_t<code_pair_t> subset_fdselect_ranges; |
328 | |
329 | hb_inc_bimap_t fdmap; |
330 | |
331 | str_buff_vec_t subset_charstrings; |
332 | str_buff_vec_t subset_globalsubrs; |
333 | hb_vector_t<str_buff_vec_t> subset_localsubrs; |
334 | |
335 | bool drop_hints; |
336 | bool desubroutinize; |
337 | }; |
338 | |
339 | static bool _serialize_cff2 (hb_serialize_context_t *c, |
340 | cff2_subset_plan &plan, |
341 | const OT::cff2::accelerator_subset_t &acc, |
342 | unsigned int num_glyphs) |
343 | { |
344 | /* private dicts & local subrs */ |
345 | hb_vector_t<table_info_t> private_dict_infos; |
346 | if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; |
347 | |
348 | for (int i = (int)acc.privateDicts.length; --i >= 0 ;) |
349 | { |
350 | if (plan.fdmap.has (i)) |
351 | { |
352 | objidx_t subrs_link = 0; |
353 | |
354 | if (plan.subset_localsubrs[i].length > 0) |
355 | { |
356 | CFF2Subrs *dest = c->start_embed <CFF2Subrs> (); |
357 | if (unlikely (!dest)) return false; |
358 | c->push (); |
359 | if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) |
360 | subrs_link = c->pop_pack (); |
361 | else |
362 | { |
363 | c->pop_discard (); |
364 | return false; |
365 | } |
366 | } |
367 | PrivateDict *pd = c->start_embed<PrivateDict> (); |
368 | if (unlikely (!pd)) return false; |
369 | c->push (); |
370 | cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); |
371 | if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) |
372 | { |
373 | unsigned fd = plan.fdmap[i]; |
374 | private_dict_infos[fd].size = c->length (); |
375 | private_dict_infos[fd].link = c->pop_pack (); |
376 | } |
377 | else |
378 | { |
379 | c->pop_discard (); |
380 | return false; |
381 | } |
382 | } |
383 | } |
384 | |
385 | /* CharStrings */ |
386 | { |
387 | CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> (); |
388 | if (unlikely (!cs)) return false; |
389 | c->push (); |
390 | if (likely (cs->serialize (c, plan.subset_charstrings))) |
391 | plan.info.char_strings_link = c->pop_pack (); |
392 | else |
393 | { |
394 | c->pop_discard (); |
395 | return false; |
396 | } |
397 | } |
398 | |
399 | /* FDSelect */ |
400 | if (acc.fdSelect != &Null (CFF2FDSelect)) |
401 | { |
402 | c->push (); |
403 | if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, plan.orig_fdcount, |
404 | plan.subset_fdselect_format, plan.subset_fdselect_size, |
405 | plan.subset_fdselect_ranges))) |
406 | plan.info.fd_select.link = c->pop_pack (); |
407 | else |
408 | { |
409 | c->pop_discard (); |
410 | return false; |
411 | } |
412 | } |
413 | |
414 | /* FDArray (FD Index) */ |
415 | { |
416 | c->push (); |
417 | CFF2FDArray *fda = c->start_embed<CFF2FDArray> (); |
418 | if (unlikely (!fda)) return false; |
419 | cff_font_dict_op_serializer_t fontSzr; |
420 | auto it = |
421 | + hb_zip (+ hb_iter (acc.fontDicts) |
422 | | hb_filter ([&] (const cff2_font_dict_values_t &_) |
423 | { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), |
424 | hb_iter (private_dict_infos)) |
425 | ; |
426 | if (unlikely (!fda->serialize (c, it, fontSzr))) return false; |
427 | plan.info.fd_array_link = c->pop_pack (); |
428 | } |
429 | |
430 | /* variation store */ |
431 | if (acc.varStore != &Null (CFF2VariationStore)) |
432 | { |
433 | c->push (); |
434 | CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> (); |
435 | if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; |
436 | plan.info.var_store_link = c->pop_pack (); |
437 | } |
438 | |
439 | OT::cff2 *cff2 = c->allocate_min<OT::cff2> (); |
440 | if (unlikely (!cff2)) return false; |
441 | |
442 | /* header */ |
443 | cff2->version.major = 0x02; |
444 | cff2->version.minor = 0x00; |
445 | cff2->topDict = OT::cff2::static_size; |
446 | |
447 | /* top dict */ |
448 | { |
449 | TopDict &dict = cff2 + cff2->topDict; |
450 | cff2_top_dict_op_serializer_t topSzr; |
451 | if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false; |
452 | cff2->topDictSize = c->head - (const char *)&dict; |
453 | } |
454 | |
455 | /* global subrs */ |
456 | { |
457 | CFF2Subrs *dest = c->start_embed <CFF2Subrs> (); |
458 | if (unlikely (!dest)) return false; |
459 | return dest->serialize (c, plan.subset_globalsubrs); |
460 | } |
461 | } |
462 | |
463 | static bool |
464 | _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, |
465 | hb_subset_context_t *c) |
466 | { |
467 | cff2_subset_plan cff2_plan; |
468 | |
469 | if (unlikely (!cff2_plan.create (acc, c->plan))) return false; |
470 | return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); |
471 | } |
472 | |
473 | /** |
474 | * hb_subset_cff2: |
475 | * Subsets the CFF2 table according to a provided subset context. |
476 | **/ |
477 | bool |
478 | hb_subset_cff2 (hb_subset_context_t *c) |
479 | { |
480 | OT::cff2::accelerator_subset_t acc; |
481 | acc.init (c->plan->source); |
482 | bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c); |
483 | acc.fini (); |
484 | |
485 | return result; |
486 | } |
487 | |
488 | #endif |
489 | |