/src/harfbuzz/src/hb-subset-cff-common.hh
Line | Count | Source |
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 | | #ifndef HB_SUBSET_CFF_COMMON_HH |
28 | | #define HB_SUBSET_CFF_COMMON_HH |
29 | | |
30 | | #include "hb.hh" |
31 | | |
32 | | #include "hb-subset-plan.hh" |
33 | | #include "hb-cff-interp-cs-common.hh" |
34 | | |
35 | | namespace CFF { |
36 | | |
37 | | /* CFF2 CharStrings may omit a tail of glyphs provided by VARC, in which |
38 | | * case FDSelect only covers the CharStrings glyphs. Use FD 0 for glyphs |
39 | | * beyond that. */ |
40 | | template <typename ACC> |
41 | | static inline unsigned |
42 | | subset_get_fd (const ACC &acc, hb_codepoint_t glyph) |
43 | 35.7k | { |
44 | 35.7k | return likely (glyph < acc.num_glyphs) ? acc.fdSelect->get_fd (glyph) : 0; |
45 | 35.7k | } hb-subset-cff1.cc:unsigned int CFF::subset_get_fd<OT::cff1::accelerator_subset_t>(OT::cff1::accelerator_subset_t const&, unsigned int) Line | Count | Source | 43 | 20.2k | { | 44 | 20.2k | return likely (glyph < acc.num_glyphs) ? acc.fdSelect->get_fd (glyph) : 0; | 45 | 20.2k | } |
hb-subset-cff2.cc:unsigned int CFF::subset_get_fd<OT::cff2::accelerator_subset_t>(OT::cff2::accelerator_subset_t const&, unsigned int) Line | Count | Source | 43 | 15.4k | { | 44 | 15.4k | return likely (glyph < acc.num_glyphs) ? acc.fdSelect->get_fd (glyph) : 0; | 45 | 15.4k | } |
|
46 | | |
47 | | /* Used for writing a temporary charstring */ |
48 | | struct str_encoder_t |
49 | | { |
50 | | str_encoder_t (str_buff_t &buff_) |
51 | 6.30M | : buff (buff_) {} |
52 | | |
53 | 25.1k | void reset () { buff.reset (); } |
54 | | |
55 | | void encode_byte (unsigned char b) |
56 | 8.07M | { |
57 | 8.07M | if (likely ((signed) buff.length < buff.allocated)) |
58 | 8.04M | buff.arrayZ[buff.length++] = b; |
59 | 27.1k | else |
60 | 27.1k | buff.push (b); |
61 | 8.07M | } |
62 | | |
63 | | void encode_int (int v) |
64 | 5.66M | { |
65 | 5.66M | if ((-1131 <= v) && (v <= 1131)) |
66 | 5.58M | { |
67 | 5.58M | if ((-107 <= v) && (v <= 107)) |
68 | 5.38M | encode_byte (v + 139); |
69 | 200k | else if (v > 0) |
70 | 143k | { |
71 | 143k | v -= 108; |
72 | 143k | encode_byte ((v >> 8) + OpCode_TwoBytePosInt0); |
73 | 143k | encode_byte (v & 0xFF); |
74 | 143k | } |
75 | 56.9k | else |
76 | 56.9k | { |
77 | 56.9k | v = -v - 108; |
78 | 56.9k | encode_byte ((v >> 8) + OpCode_TwoByteNegInt0); |
79 | 56.9k | encode_byte (v & 0xFF); |
80 | 56.9k | } |
81 | 5.58M | } |
82 | 77.6k | else |
83 | 77.6k | { |
84 | 77.6k | if (unlikely (v < -32768)) |
85 | 9 | v = -32768; |
86 | 77.6k | else if (unlikely (v > 32767)) |
87 | 52 | v = 32767; |
88 | 77.6k | encode_byte (OpCode_shortint); |
89 | 77.6k | encode_byte ((v >> 8) & 0xFF); |
90 | 77.6k | encode_byte (v & 0xFF); |
91 | 77.6k | } |
92 | 5.66M | } |
93 | | |
94 | | // Encode number for CharString |
95 | | void encode_num_cs (const number_t& n) |
96 | 5.89M | { |
97 | 5.89M | if (n.in_int16_range ()) |
98 | 5.59M | encode_int (n.to_int ()); |
99 | 298k | else |
100 | 298k | { |
101 | 298k | encode_byte (OpCode_fixedcs); |
102 | 298k | encode_int32_payload (n.to_fixed ()); |
103 | 298k | } |
104 | 5.89M | } |
105 | | |
106 | | void encode_int32_payload (int32_t v) |
107 | 298k | { |
108 | 298k | encode_byte ((v >> 24) & 0xFF); |
109 | 298k | encode_byte ((v >> 16) & 0xFF); |
110 | 298k | encode_byte ((v >> 8) & 0xFF); |
111 | 298k | encode_byte (v & 0xFF); |
112 | 298k | } |
113 | | |
114 | | // Encode number for TopDict / Private |
115 | | void encode_num_tp (const number_t& n) |
116 | 44.4k | { |
117 | 44.4k | if (n.in_int16_range ()) |
118 | 43.9k | encode_int (n.to_int ()); |
119 | 479 | else if (n.in_int32_range ()) |
120 | 150 | { |
121 | 150 | encode_byte (OpCode_longintdict); |
122 | 150 | encode_int32_payload (n.to_int ()); |
123 | 150 | } |
124 | 329 | else |
125 | 329 | { |
126 | | // Sigh. BCD |
127 | | // https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions |
128 | 329 | double v = n.to_real (); |
129 | | |
130 | | // Based on: |
131 | | // https://github.com/fonttools/fonttools/blob/0738c41dfbcbc213ab9263f486ef0cccc6eb5ce5/Lib/fontTools/misc/psCharStrings.py#L267-L316 |
132 | | |
133 | 329 | char buf[16]; |
134 | | /* FontTools has the following comment: |
135 | | * |
136 | | * # Note: 14 decimal digits seems to be the limitation for CFF real numbers |
137 | | * # in macOS. However, we use 8 here to match the implementation of AFDKO. |
138 | | * |
139 | | * We use 8 here to match FontTools X-). |
140 | | */ |
141 | | |
142 | 329 | hb_locale_t clocale HB_UNUSED; |
143 | 329 | hb_locale_t oldlocale HB_UNUSED; |
144 | 329 | oldlocale = hb_uselocale (clocale = newlocale (LC_ALL_MASK, "C", NULL)); |
145 | 329 | int n_printed = snprintf (buf, sizeof (buf), "%.8G", v); |
146 | 329 | (void) hb_uselocale (((void) freelocale (clocale), oldlocale)); |
147 | | |
148 | | /* Defensive: buf[16] exactly fits the widest %.8G output (15 chars |
149 | | * + NUL); bail cleanly if snprintf ever errors or truncates. */ |
150 | 329 | if (unlikely (n_printed < 0 || (size_t) n_printed >= sizeof (buf))) |
151 | 0 | return; |
152 | | |
153 | 329 | char *s = buf; |
154 | 329 | size_t len; |
155 | 329 | char *comma = strchr (s, ','); |
156 | 329 | if (comma) // Comma for some European locales in case no uselocale available. |
157 | 0 | *comma = '.'; |
158 | 329 | if (s[0] == '0' && s[1] == '.') |
159 | 261 | s++; |
160 | 68 | else if (s[0] == '-' && s[1] == '0' && s[2] == '.') |
161 | 27 | { |
162 | 27 | s[1] = '-'; |
163 | 27 | s++; |
164 | 27 | } |
165 | 41 | else if ((len = strlen (s)) > 3 && !strcmp (s + len - 3, "000")) |
166 | 2 | { |
167 | | /* Count trailing zeros from the end. s[0] is guaranteed non-'0' |
168 | | * here (blocks 1-2 handle "0.xxx"/"-0.xxx" first, and %G never |
169 | | * emits trailing fractional zeros), so the backward scan always |
170 | | * terminates before reaching s. */ |
171 | 2 | unsigned exponent = 0; |
172 | 2 | char *s2 = s + len - 1; |
173 | 8 | while (*s2 == '0') |
174 | 6 | { |
175 | 6 | s2--; |
176 | 6 | exponent++; |
177 | 6 | } |
178 | 2 | snprintf (s2 + 1, sizeof (buf) - (s2 + 1 - buf), "E%u", exponent); |
179 | 2 | } |
180 | 39 | else |
181 | 39 | { |
182 | 39 | char *dot = strchr (s, '.'); |
183 | 39 | char *e = strchr (s, 'E'); |
184 | 39 | if (dot && e) |
185 | 2 | { |
186 | 2 | memmove (dot, dot + 1, e - (dot + 1)); |
187 | 2 | int exponent = atoi (e + 1); |
188 | 2 | int new_exponent = exponent - (e - (dot + 1)); |
189 | 2 | if (new_exponent == 1) |
190 | 0 | { |
191 | 0 | e[-1] = '0'; |
192 | 0 | e[0] = '\0'; |
193 | 0 | } |
194 | 2 | else |
195 | 2 | snprintf (e - 1, sizeof (buf) - (e - 1 - buf), "E%d", new_exponent); |
196 | 2 | } |
197 | 39 | } |
198 | 329 | if ((s[0] == '.' && s[1] == '0') || (s[0] == '-' && s[1] == '.' && s[2] == '0')) |
199 | 282 | { |
200 | 282 | int sign = s[0] == '-'; |
201 | 282 | char *s2 = s + sign + 1; |
202 | 617 | while (*s2 == '0') |
203 | 335 | s2++; |
204 | 282 | len = strlen (s2); |
205 | 282 | memmove (s + sign, s2, len); |
206 | 282 | snprintf (s + sign + len, sizeof (buf) - (s + sign + len - buf), "E-%u", (unsigned) (strlen (s + sign) - 1)); |
207 | 282 | } |
208 | | /* Nibble capacity must cover widest %.8G output plus END and pad */ |
209 | 329 | nibble_buffer_t<ARRAY_LENGTH (buf) + 2> nibbles; |
210 | 1.92k | while (*s) |
211 | 1.59k | { |
212 | 1.59k | char c = s[0]; |
213 | 1.59k | s++; |
214 | | |
215 | 1.59k | switch (c) |
216 | 1.59k | { |
217 | 304 | case 'E': |
218 | 304 | { |
219 | 304 | char c2 = *s; |
220 | 304 | if (c2 == '-') |
221 | 287 | { |
222 | 287 | s++; |
223 | 287 | nibbles.push (0x0C); // E- |
224 | 287 | } else { |
225 | 17 | if (c2 == '+') |
226 | 14 | s++; |
227 | 17 | nibbles.push (0x0B); // E |
228 | 17 | } |
229 | 304 | if (*s == '0') |
230 | 3 | s++; |
231 | 304 | continue; |
232 | 0 | } |
233 | | |
234 | 9 | case '.': |
235 | 9 | nibbles.push (0x0A); // . |
236 | 9 | continue; |
237 | | |
238 | 28 | case '-': |
239 | 28 | nibbles.push (0x0E); // - |
240 | 28 | continue; |
241 | 1.59k | } |
242 | | |
243 | 1.25k | nibbles.push (c - '0'); |
244 | 1.25k | } |
245 | | |
246 | 329 | hb_ubytes_t bytes = nibbles.commit (); |
247 | 329 | if (unlikely (bytes.length == 0)) { buff.set_error (); return; } |
248 | 329 | encode_byte (OpCode_BCD); |
249 | 329 | copy_str (bytes); |
250 | 329 | } |
251 | 44.4k | } |
252 | | |
253 | | void encode_op (op_code_t op) |
254 | 443k | { |
255 | 443k | if (Is_OpCode_ESC (op)) |
256 | 83.2k | { |
257 | 83.2k | encode_byte (OpCode_escape); |
258 | 83.2k | encode_byte (Unmake_OpCode_ESC (op)); |
259 | 83.2k | } |
260 | 360k | else |
261 | 360k | encode_byte (op); |
262 | 443k | } |
263 | | |
264 | | void copy_str (const hb_ubytes_t str) |
265 | 72.0k | { |
266 | 72.0k | unsigned buff_tip = buff.length; |
267 | 72.0k | if (likely (buff.resize_dirty (buff_tip + str.length))) |
268 | 72.0k | hb_memcpy (buff.arrayZ + buff_tip, str, str.length); |
269 | 0 | else buff.ensure_error (); |
270 | 72.0k | } |
271 | | |
272 | 29.5k | bool in_error () const { return buff.in_error (); } |
273 | | |
274 | | protected: |
275 | | str_buff_t &buff; |
276 | | private: |
277 | | template <unsigned size> |
278 | | struct nibble_buffer_t |
279 | | { |
280 | | static_assert (0 < size && size < 64, ""); |
281 | | void push (uint8_t nibble) |
282 | 1.94k | { |
283 | 1.94k | if (unlikely (in_error || nibble_count >= size)) { in_error = true; return; } |
284 | 1.94k | if (!(nibble_count & 1)) |
285 | 971 | bytes[nibble_count >> 1] = nibble << 4; |
286 | 971 | else |
287 | 971 | bytes[nibble_count >> 1] |= nibble; |
288 | 1.94k | nibble_count++; |
289 | 1.94k | } |
290 | | hb_ubytes_t commit () |
291 | 329 | { |
292 | 329 | push (0x0F); |
293 | 329 | if (nibble_count & 1) push (0x0F); |
294 | 329 | return unlikely (in_error) ? hb_ubytes_t () : hb_ubytes_t (bytes, nibble_count >> 1); |
295 | 329 | } |
296 | | private: |
297 | | bool in_error = false; |
298 | | uint8_t bytes[(size + 1) / 2]; |
299 | | unsigned nibble_count = 0; |
300 | | }; |
301 | | }; |
302 | | |
303 | | struct cff_sub_table_info_t { |
304 | | cff_sub_table_info_t () |
305 | 9.23k | : fd_array_link (0), |
306 | 9.23k | char_strings_link (0) |
307 | 9.23k | { |
308 | 9.23k | fd_select.init (); |
309 | 9.23k | } |
310 | | |
311 | | table_info_t fd_select; |
312 | | objidx_t fd_array_link; |
313 | | objidx_t char_strings_link; |
314 | | }; |
315 | | |
316 | | template <typename OPSTR=op_str_t> |
317 | | struct cff_top_dict_op_serializer_t : op_serializer_t |
318 | | { |
319 | | bool serialize (hb_serialize_context_t *c, |
320 | | const OPSTR &opstr, |
321 | | const cff_sub_table_info_t &info) const |
322 | 12.5k | { |
323 | 12.5k | TRACE_SERIALIZE (this); |
324 | | |
325 | 12.5k | switch (opstr.op) |
326 | 12.5k | { |
327 | 3.23k | case OpCode_CharStrings: |
328 | 3.23k | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); |
329 | | |
330 | 2.14k | case OpCode_FDArray: |
331 | 2.14k | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); |
332 | | |
333 | 436 | case OpCode_FDSelect: |
334 | 436 | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); |
335 | | |
336 | 6.69k | default: |
337 | 6.69k | return_trace (copy_opstr (c, opstr)); |
338 | 12.5k | } |
339 | 12.5k | return_trace (true); |
340 | 12.5k | } CFF::cff_top_dict_op_serializer_t<CFF::cff1_top_dict_val_t>::serialize(hb_serialize_context_t*, CFF::cff1_top_dict_val_t const&, CFF::cff_sub_table_info_t const&) const Line | Count | Source | 322 | 4.87k | { | 323 | 4.87k | TRACE_SERIALIZE (this); | 324 | | | 325 | 4.87k | switch (opstr.op) | 326 | 4.87k | { | 327 | 1.27k | case OpCode_CharStrings: | 328 | 1.27k | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); | 329 | | | 330 | 218 | case OpCode_FDArray: | 331 | 218 | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); | 332 | | | 333 | 214 | case OpCode_FDSelect: | 334 | 214 | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); | 335 | | | 336 | 3.16k | default: | 337 | 3.16k | return_trace (copy_opstr (c, opstr)); | 338 | 4.87k | } | 339 | 0 | return_trace (true); | 340 | 4.87k | } |
CFF::cff_top_dict_op_serializer_t<CFF::op_str_t>::serialize(hb_serialize_context_t*, CFF::op_str_t const&, CFF::cff_sub_table_info_t const&) const Line | Count | Source | 322 | 7.62k | { | 323 | 7.62k | TRACE_SERIALIZE (this); | 324 | | | 325 | 7.62k | switch (opstr.op) | 326 | 7.62k | { | 327 | 1.95k | case OpCode_CharStrings: | 328 | 1.95k | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); | 329 | | | 330 | 1.92k | case OpCode_FDArray: | 331 | 1.92k | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); | 332 | | | 333 | 222 | case OpCode_FDSelect: | 334 | 222 | return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); | 335 | | | 336 | 3.52k | default: | 337 | 3.52k | return_trace (copy_opstr (c, opstr)); | 338 | 7.62k | } | 339 | 0 | return_trace (true); | 340 | 7.62k | } |
|
341 | | }; |
342 | | |
343 | | struct cff_font_dict_op_serializer_t : op_serializer_t |
344 | | { |
345 | | bool serialize (hb_serialize_context_t *c, |
346 | | const op_str_t &opstr, |
347 | | const table_info_t &privateDictInfo) const |
348 | 26.9k | { |
349 | 26.9k | TRACE_SERIALIZE (this); |
350 | | |
351 | 26.9k | if (opstr.op == OpCode_Private) |
352 | 2.33k | { |
353 | | /* serialize the private dict size & offset as 2-byte & 4-byte integers */ |
354 | 2.33k | return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) && |
355 | 2.33k | Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute)); |
356 | 2.33k | } |
357 | 24.5k | else |
358 | 24.5k | { |
359 | 24.5k | unsigned char *d = c->allocate_size<unsigned char> (opstr.length); |
360 | 24.5k | if (unlikely (!d)) return_trace (false); |
361 | | /* Faster than hb_memcpy for small strings. */ |
362 | 81.0k | for (unsigned i = 0; i < opstr.length; i++) |
363 | 56.5k | d[i] = opstr.ptr[i]; |
364 | | //hb_memcpy (d, opstr.ptr, opstr.length); |
365 | 24.5k | } |
366 | 26.9k | return_trace (true); |
367 | 26.9k | } |
368 | | }; |
369 | | |
370 | | /* CharString command for specialization */ |
371 | | struct cs_command_t |
372 | | { |
373 | | hb_vector_t<number_t> args; |
374 | | hb_vector_t<unsigned char> mask_bytes; /* For hintmask/cntrmask payload bytes. */ |
375 | | op_code_t op; |
376 | | |
377 | 31.5k | cs_command_t () : op (OpCode_Invalid) {} |
378 | 14.0k | cs_command_t (op_code_t op_) : op (op_) {} |
379 | | }; |
380 | | |
381 | | typedef hb_vector_t<cs_command_t> *cs_command_vec_t; |
382 | | |
383 | | struct cff2_instancing_plan_t; |
384 | | struct cff2_cs_rounding_t; |
385 | | |
386 | | struct flatten_param_t |
387 | | { |
388 | | flatten_param_t (str_buff_t &flatStr_, |
389 | | bool drop_hints_, |
390 | | const hb_subset_plan_t *plan_, |
391 | | cs_command_vec_t commands_ = nullptr) |
392 | 5.27k | : flatStr (flatStr_), drop_hints (drop_hints_), plan (plan_), commands (commands_) {} |
393 | | |
394 | | str_buff_t &flatStr; |
395 | | bool drop_hints; |
396 | | const hb_subset_plan_t *plan; |
397 | | cs_command_vec_t commands; /* Optional: capture parsed commands for specialization */ |
398 | | |
399 | | /* CFF2 partial instancing: when set, blends are rewritten against the |
400 | | * instanced variation store instead of being copied or flattened. */ |
401 | | const cff2_instancing_plan_t *instancer = nullptr; |
402 | | cff2_cs_rounding_t *rounding = nullptr; |
403 | | bool emitted_blend = false; |
404 | | }; |
405 | | |
406 | | template <typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid> |
407 | | struct subr_flattener_t |
408 | | { |
409 | | subr_flattener_t (const ACC &acc_, |
410 | | const hb_subset_plan_t *plan_) |
411 | 1.88k | : acc (acc_), plan (plan_) {}CFF::subr_flattener_t<OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, 14u>::subr_flattener_t(OT::cff1::accelerator_subset_t const&, hb_subset_plan_t const*) Line | Count | Source | 411 | 1.35k | : acc (acc_), plan (plan_) {} |
CFF::subr_flattener_t<OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_flatten_t, 65535u>::subr_flattener_t(OT::cff2::accelerator_subset_t const&, hb_subset_plan_t const*) Line | Count | Source | 411 | 535 | : acc (acc_), plan (plan_) {} |
|
412 | | |
413 | | bool flatten (str_buff_vec_t &flat_charstrings, |
414 | | hb_vector_t<hb_vector_t<cs_command_t>> *command_capture = nullptr) |
415 | 1.88k | { |
416 | 1.88k | unsigned count = plan->num_output_glyphs (); |
417 | 1.88k | if (!flat_charstrings.resize_exact (count)) |
418 | 1 | return false; |
419 | 1.88k | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); |
420 | 282k | for (unsigned int i = 0; i < count; i++) |
421 | 281k | { |
422 | 281k | hb_codepoint_t glyph; |
423 | 281k | if (!plan->old_gid_for_new_gid (i, &glyph)) |
424 | 278k | { |
425 | | /* add an endchar only charstring for a missing glyph if CFF1 */ |
426 | 278k | if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); |
427 | 278k | continue; |
428 | 278k | } |
429 | 2.62k | const hb_ubytes_t str = (*acc.charStrings)[glyph]; |
430 | 2.62k | unsigned int fd = subset_get_fd (acc, glyph); |
431 | 2.62k | if (unlikely (fd >= acc.fdCount)) |
432 | 7 | return false; |
433 | | |
434 | | |
435 | 2.61k | ENV env (str, acc, fd, |
436 | 2.61k | plan->normalized_coords.arrayZ, plan->normalized_coords.length); |
437 | 2.61k | cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env); |
438 | 2.61k | flatten_param_t param = { |
439 | 2.61k | flat_charstrings.arrayZ[i], |
440 | 2.61k | (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING), |
441 | 2.61k | plan, |
442 | 2.61k | command_capture ? &(*command_capture)[i] : nullptr |
443 | 2.61k | }; |
444 | 2.61k | if (unlikely (!interp.interpret (param, budget))) |
445 | 311 | return false; |
446 | 2.61k | } |
447 | 1.56k | return true; |
448 | 1.88k | } CFF::subr_flattener_t<OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, 14u>::flatten(hb_vector_t<hb_vector_t<unsigned char, false>, false>&, hb_vector_t<hb_vector_t<CFF::cs_command_t, false>, false>*) Line | Count | Source | 415 | 1.35k | { | 416 | 1.35k | unsigned count = plan->num_output_glyphs (); | 417 | 1.35k | if (!flat_charstrings.resize_exact (count)) | 418 | 1 | return false; | 419 | 1.35k | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); | 420 | 67.8k | for (unsigned int i = 0; i < count; i++) | 421 | 66.6k | { | 422 | 66.6k | hb_codepoint_t glyph; | 423 | 66.6k | if (!plan->old_gid_for_new_gid (i, &glyph)) | 424 | 64.7k | { | 425 | | /* add an endchar only charstring for a missing glyph if CFF1 */ | 426 | 64.7k | if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); | 427 | 64.7k | continue; | 428 | 64.7k | } | 429 | 1.93k | const hb_ubytes_t str = (*acc.charStrings)[glyph]; | 430 | 1.93k | unsigned int fd = subset_get_fd (acc, glyph); | 431 | 1.93k | if (unlikely (fd >= acc.fdCount)) | 432 | 5 | return false; | 433 | | | 434 | | | 435 | 1.92k | ENV env (str, acc, fd, | 436 | 1.92k | plan->normalized_coords.arrayZ, plan->normalized_coords.length); | 437 | 1.92k | cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env); | 438 | 1.92k | flatten_param_t param = { | 439 | 1.92k | flat_charstrings.arrayZ[i], | 440 | 1.92k | (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING), | 441 | 1.92k | plan, | 442 | 1.92k | command_capture ? &(*command_capture)[i] : nullptr | 443 | 1.92k | }; | 444 | 1.92k | if (unlikely (!interp.interpret (param, budget))) | 445 | 193 | return false; | 446 | 1.92k | } | 447 | 1.15k | return true; | 448 | 1.35k | } |
CFF::subr_flattener_t<OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_flatten_t, 65535u>::flatten(hb_vector_t<hb_vector_t<unsigned char, false>, false>&, hb_vector_t<hb_vector_t<CFF::cs_command_t, false>, false>*) Line | Count | Source | 415 | 535 | { | 416 | 535 | unsigned count = plan->num_output_glyphs (); | 417 | 535 | if (!flat_charstrings.resize_exact (count)) | 418 | 0 | return false; | 419 | 535 | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); | 420 | 215k | for (unsigned int i = 0; i < count; i++) | 421 | 214k | { | 422 | 214k | hb_codepoint_t glyph; | 423 | 214k | if (!plan->old_gid_for_new_gid (i, &glyph)) | 424 | 214k | { | 425 | | /* add an endchar only charstring for a missing glyph if CFF1 */ | 426 | 214k | if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); | 427 | 214k | continue; | 428 | 214k | } | 429 | 692 | const hb_ubytes_t str = (*acc.charStrings)[glyph]; | 430 | 692 | unsigned int fd = subset_get_fd (acc, glyph); | 431 | 692 | if (unlikely (fd >= acc.fdCount)) | 432 | 2 | return false; | 433 | | | 434 | | | 435 | 690 | ENV env (str, acc, fd, | 436 | 690 | plan->normalized_coords.arrayZ, plan->normalized_coords.length); | 437 | 690 | cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env); | 438 | 690 | flatten_param_t param = { | 439 | 690 | flat_charstrings.arrayZ[i], | 440 | 690 | (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING), | 441 | 690 | plan, | 442 | 690 | command_capture ? &(*command_capture)[i] : nullptr | 443 | 690 | }; | 444 | 690 | if (unlikely (!interp.interpret (param, budget))) | 445 | 118 | return false; | 446 | 690 | } | 447 | 415 | return true; | 448 | 535 | } |
|
449 | | |
450 | | const ACC &acc; |
451 | | const hb_subset_plan_t *plan; |
452 | | }; |
453 | | |
454 | | struct subr_closures_t |
455 | | { |
456 | 6.10k | subr_closures_t (unsigned int fd_count) : global_closure (), local_closures () |
457 | 6.10k | { |
458 | 6.10k | local_closures.resize_exact (fd_count); |
459 | 6.10k | } |
460 | | |
461 | | void reset () |
462 | 948 | { |
463 | 948 | global_closure.clear(); |
464 | 2.51k | for (unsigned int i = 0; i < local_closures.length; i++) |
465 | 1.56k | local_closures[i].clear(); |
466 | 948 | } |
467 | | |
468 | 6.09k | bool in_error () const { return local_closures.in_error (); } |
469 | | hb_set_t global_closure; |
470 | | hb_vector_t<hb_set_t> local_closures; |
471 | | }; |
472 | | |
473 | | struct parsed_cs_op_t : op_str_t |
474 | | { |
475 | | parsed_cs_op_t (unsigned int subr_num_ = 0) : |
476 | 7.95M | subr_num (subr_num_) {} |
477 | | |
478 | 223k | bool is_hinting () const { return hinting_flag; } |
479 | 44.9k | void set_hinting () { hinting_flag = true; } |
480 | | |
481 | | /* Metadata for retained charstring tokens. */ |
482 | | |
483 | | protected: |
484 | | bool hinting_flag = false; |
485 | | |
486 | | public: |
487 | | uint16_t subr_num; |
488 | | }; |
489 | | |
490 | | struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t> |
491 | | { |
492 | | parsed_cs_str_t () : |
493 | 1.64M | parsed (false), |
494 | 1.64M | hint_dropped (false), |
495 | 1.64M | has_prefix_ (false), |
496 | 1.64M | has_calls_ (false), |
497 | 1.64M | coalescing_ (false) |
498 | 1.64M | { |
499 | 1.64M | SUPER::init (); |
500 | 1.64M | } |
501 | | |
502 | | HB_ALWAYS_INLINE |
503 | | void add_op (op_code_t op, const byte_str_ref_t& str_ref) |
504 | 77.7M | { |
505 | 77.7M | if (is_parsed ()) return; |
506 | 77.6M | last_op_ = op; |
507 | 77.6M | if (coalescing_) |
508 | 69.7M | { |
509 | | /* Do not record individual tokens; only note their boundaries. |
510 | | * Bytes are flushed as verbatim segments at call sites and at |
511 | | * the end of the string. Only enabled when per-op granularity |
512 | | * is not needed later for hint analysis. */ |
513 | 69.7M | penultimate_end_ = last_end_; |
514 | 69.7M | last_end_ = str_ref.get_offset (); |
515 | 69.7M | if (unlikely (op == OpCode_return || op == OpCode_endchar)) |
516 | 8.93k | flush_segment (str_ref, last_end_); |
517 | 69.7M | return; |
518 | 69.7M | } |
519 | 7.89M | SUPER::add_op (op, str_ref); |
520 | 7.89M | } |
521 | | |
522 | | bool has_immediate_subr_number () const |
523 | 29.0k | { |
524 | 29.0k | if (is_parsed ()) return true; |
525 | 25.1k | op_code_t op = coalescing_ ? last_op_ : |
526 | 25.1k | values.length ? values.tail ().op : OpCode_Invalid; |
527 | 25.1k | return op == OpCode_shortint || op == OpCode_fixedcs || |
528 | 25.1k | (OpCode_OneByteIntFirst <= op && op <= OpCode_TwoByteNegInt3); |
529 | 29.0k | } |
530 | | |
531 | | void add_call_op (op_code_t op, const byte_str_ref_t& str_ref, unsigned int subr_num) |
532 | 28.9k | { |
533 | 28.9k | if (is_parsed ()) return; |
534 | 25.0k | has_calls_ = true; |
535 | 25.0k | last_op_ = op; |
536 | | |
537 | 25.0k | if (coalescing_) |
538 | 22.3k | { |
539 | | /* Flush bytes preceding the subroutine-number token, then skip |
540 | | * over the number: it is re-encoded with the new bias. */ |
541 | 22.3k | flush_segment (str_ref, penultimate_end_); |
542 | 22.3k | opStart = last_end_; |
543 | 22.3k | SUPER::add_op (op, str_ref, {subr_num}); |
544 | 22.3k | penultimate_end_ = last_end_ = str_ref.get_offset (); |
545 | 22.3k | return; |
546 | 22.3k | } |
547 | | |
548 | | /* Pop the subroutine number. */ |
549 | 2.66k | values.pop (); |
550 | | |
551 | 2.66k | SUPER::add_op (op, str_ref, {subr_num}); |
552 | 2.66k | } |
553 | | |
554 | | /* Flush pending bytes [opStart, end) as verbatim segment entries. */ |
555 | | void flush_segment (const byte_str_ref_t& str_ref, unsigned end) |
556 | 46.5k | { |
557 | 46.5k | unsigned start = opStart; |
558 | 46.5k | if (end <= start) return; |
559 | 73.1k | while (start < end) |
560 | 36.7k | { |
561 | 36.7k | auto arr = str_ref.sub_array (start, hb_min (end - start, 255u)); |
562 | 36.7k | if (unlikely (!arr.length)) break; |
563 | 36.7k | parsed_cs_op_t *val = values.push (); |
564 | 36.7k | val->ptr = arr.arrayZ; |
565 | 36.7k | val->length = arr.length; |
566 | 36.7k | start += arr.length; |
567 | 36.7k | } |
568 | 36.4k | opStart = end; |
569 | 36.4k | } |
570 | | |
571 | | /* For coalescing mode: flush any pending bytes through the current |
572 | | * position; used where the string ends without an explicit |
573 | | * return/endchar op (CFF2). */ |
574 | | void flush_coalesced (const byte_str_ref_t& str_ref) |
575 | 27.9k | { |
576 | 27.9k | if (coalescing_ && !is_parsed ()) |
577 | 15.2k | flush_segment (str_ref, str_ref.get_offset ()); |
578 | 27.9k | } |
579 | | |
580 | 47.1k | void enable_coalescing () { coalescing_ = true; } |
581 | 0 | bool is_coalescing () const { return coalescing_; } |
582 | | |
583 | | void set_prefix (const number_t &num, op_code_t op = OpCode_Invalid) |
584 | 3.24k | { |
585 | 3.24k | has_prefix_ = true; |
586 | 3.24k | prefix_op_ = op; |
587 | 3.24k | prefix_num_ = num; |
588 | 3.24k | } |
589 | | |
590 | | bool at_end (unsigned int pos) const |
591 | 8.45k | { |
592 | 8.45k | return ((pos + 1 >= values.length) /* CFF2 */ |
593 | 8.36k | || (values[pos + 1].op == OpCode_return)); |
594 | 8.45k | } |
595 | | |
596 | 77.8M | bool is_parsed () const { return parsed; } |
597 | 49.3k | void set_parsed () { parsed = true; } |
598 | | |
599 | 405 | bool is_hint_dropped () const { return hint_dropped; } |
600 | 831 | void set_hint_dropped () { hint_dropped = true; } |
601 | | |
602 | 0 | bool is_vsindex_dropped () const { return vsindex_dropped; } |
603 | 48 | void set_vsindex_dropped () { vsindex_dropped = true; } |
604 | | |
605 | 25.1k | bool has_prefix () const { return has_prefix_; } |
606 | 294 | op_code_t prefix_op () const { return prefix_op_; } |
607 | 289 | const number_t &prefix_num () const { return prefix_num_; } |
608 | | |
609 | 2.33k | bool has_calls () const { return has_calls_; } |
610 | | |
611 | | void compact () |
612 | 1.40k | { |
613 | 1.40k | unsigned count = values.length; |
614 | 1.40k | if (!count) return; |
615 | 1.26k | auto &opstr = values.arrayZ; |
616 | 1.26k | unsigned j = 0; |
617 | 52.0k | for (unsigned i = 1; i < count; i++) |
618 | 50.7k | { |
619 | | /* See if we can combine op j and op i. */ |
620 | 50.7k | bool combine = |
621 | 50.7k | (opstr[j].op != OpCode_callsubr && opstr[j].op != OpCode_callgsubr) && |
622 | 49.5k | (opstr[i].op != OpCode_callsubr && opstr[i].op != OpCode_callgsubr) && |
623 | 48.4k | (opstr[j].is_hinting () == opstr[i].is_hinting ()) && |
624 | 47.2k | (opstr[j].ptr + opstr[j].length == opstr[i].ptr) && |
625 | 47.2k | (opstr[j].length + opstr[i].length <= 255); |
626 | | |
627 | 50.7k | if (combine) |
628 | 47.1k | { |
629 | 47.1k | opstr[j].length += opstr[i].length; |
630 | 47.1k | opstr[j].op = OpCode_Invalid; |
631 | 47.1k | } |
632 | 3.57k | else |
633 | 3.57k | { |
634 | 3.57k | opstr[++j] = opstr[i]; |
635 | 3.57k | } |
636 | 50.7k | } |
637 | 1.26k | values.shrink (j + 1); |
638 | 1.26k | } |
639 | | |
640 | | protected: |
641 | | bool parsed : 1; |
642 | | bool hint_dropped : 1; |
643 | | bool vsindex_dropped : 1; |
644 | | bool has_prefix_ : 1; |
645 | | bool has_calls_ : 1; |
646 | | /* Record verbatim byte segments instead of individual tokens, |
647 | | * making a separate compact() pass unnecessary; incompatible with |
648 | | * hint analysis. */ |
649 | | bool coalescing_ : 1; |
650 | | /* End offsets of the last two tokens seen (coalescing mode). */ |
651 | | op_code_t last_op_ = OpCode_Invalid; |
652 | | unsigned penultimate_end_ = 0; |
653 | | unsigned last_end_ = 0; |
654 | | op_code_t prefix_op_; |
655 | | number_t prefix_num_; |
656 | | |
657 | | private: |
658 | | typedef parsed_values_t<parsed_cs_op_t> SUPER; |
659 | | }; |
660 | | |
661 | | struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t> |
662 | | { |
663 | | private: |
664 | | typedef hb_vector_t<parsed_cs_str_t> SUPER; |
665 | | }; |
666 | | |
667 | | struct cff_subset_accelerator_t |
668 | | { |
669 | | static cff_subset_accelerator_t* create ( |
670 | | hb_blob_t* original_blob, |
671 | | const parsed_cs_str_vec_t& parsed_charstrings, |
672 | | const parsed_cs_str_vec_t& parsed_global_subrs, |
673 | 0 | const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) { |
674 | 0 | cff_subset_accelerator_t* accel = |
675 | 0 | (cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t)); |
676 | 0 | if (unlikely (!accel)) return nullptr; |
677 | 0 | new (accel) cff_subset_accelerator_t (original_blob, |
678 | 0 | parsed_charstrings, |
679 | 0 | parsed_global_subrs, |
680 | 0 | parsed_local_subrs); |
681 | 0 | return accel; |
682 | 0 | } |
683 | | |
684 | 0 | static void destroy (void* value) { |
685 | 0 | if (!value) return; |
686 | | |
687 | 0 | cff_subset_accelerator_t* accel = (cff_subset_accelerator_t*) value; |
688 | 0 | accel->~cff_subset_accelerator_t (); |
689 | 0 | hb_free (accel); |
690 | 0 | } |
691 | | |
692 | | cff_subset_accelerator_t( |
693 | | hb_blob_t* original_blob_, |
694 | | const parsed_cs_str_vec_t& parsed_charstrings_, |
695 | | const parsed_cs_str_vec_t& parsed_global_subrs_, |
696 | | const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs_) |
697 | 0 | { |
698 | 0 | parsed_charstrings = parsed_charstrings_; |
699 | 0 | parsed_global_subrs = parsed_global_subrs_; |
700 | 0 | parsed_local_subrs = parsed_local_subrs_; |
701 | | |
702 | | // the parsed charstrings point to memory in the original CFF table so we must hold a reference |
703 | | // to it to keep the memory valid. |
704 | 0 | original_blob = hb_blob_reference (original_blob_); |
705 | 0 | } |
706 | | |
707 | | ~cff_subset_accelerator_t() |
708 | 0 | { |
709 | 0 | hb_blob_destroy (original_blob); |
710 | 0 | auto *mapping = glyph_to_sid_map.get_relaxed (); |
711 | 0 | if (mapping) |
712 | 0 | { |
713 | 0 | mapping->~glyph_to_sid_map_t (); |
714 | 0 | hb_free (mapping); |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | | parsed_cs_str_vec_t parsed_charstrings; |
719 | | parsed_cs_str_vec_t parsed_global_subrs; |
720 | | hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs; |
721 | | mutable hb_atomic_t<glyph_to_sid_map_t *> glyph_to_sid_map; |
722 | | |
723 | | private: |
724 | | hb_blob_t* original_blob; |
725 | | }; |
726 | | |
727 | | struct subr_subset_param_t |
728 | | { |
729 | | subr_subset_param_t (parsed_cs_str_t *parsed_charstring_, |
730 | | parsed_cs_str_vec_t *parsed_global_subrs_, |
731 | | parsed_cs_str_vec_t *parsed_local_subrs_, |
732 | | hb_set_t *global_closure_, |
733 | | hb_set_t *local_closure_, |
734 | | bool drop_hints_, |
735 | | bool coalesce_ = false) : |
736 | 16.2k | current_parsed_str (parsed_charstring_), |
737 | 16.2k | parsed_charstring (parsed_charstring_), |
738 | 16.2k | parsed_global_subrs (parsed_global_subrs_), |
739 | 16.2k | parsed_local_subrs (parsed_local_subrs_), |
740 | 16.2k | global_closure (global_closure_), |
741 | 16.2k | local_closure (local_closure_), |
742 | 16.2k | drop_hints (drop_hints_), |
743 | 16.2k | coalesce (coalesce_) |
744 | 16.2k | { |
745 | 16.2k | if (coalesce) parsed_charstring->enable_coalescing (); |
746 | 16.2k | } |
747 | | |
748 | | parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context) |
749 | 57.1k | { |
750 | 57.1k | switch (context.type) |
751 | 57.1k | { |
752 | 11.0k | case CSType_CharString: |
753 | 11.0k | return parsed_charstring; |
754 | | |
755 | 42.5k | case CSType_LocalSubr: |
756 | 42.5k | if (likely (context.subr_num < parsed_local_subrs->length)) |
757 | 42.5k | return &(*parsed_local_subrs)[context.subr_num]; |
758 | 0 | break; |
759 | | |
760 | 3.51k | case CSType_GlobalSubr: |
761 | 3.51k | if (likely (context.subr_num < parsed_global_subrs->length)) |
762 | 3.51k | return &(*parsed_global_subrs)[context.subr_num]; |
763 | 0 | break; |
764 | 57.1k | } |
765 | 0 | return nullptr; |
766 | 57.1k | } |
767 | | |
768 | | template <typename ENV> |
769 | | void set_current_str (ENV &env, bool calling) |
770 | 56.3k | { |
771 | 56.3k | parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); |
772 | 56.3k | if (unlikely (!parsed_str)) |
773 | 0 | { |
774 | 0 | env.set_error (); |
775 | 0 | return; |
776 | 0 | } |
777 | | /* If the called subroutine is parsed partially but not completely yet, |
778 | | * it must be because we are calling it recursively. |
779 | | * Handle it as an error. */ |
780 | 56.3k | if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) |
781 | 201 | env.set_error (); |
782 | 56.1k | else |
783 | 56.1k | { |
784 | 56.1k | if (!parsed_str->is_parsed ()) |
785 | 39.3k | { |
786 | 39.3k | parsed_str->alloc (env.str_ref.total_size ()); |
787 | 39.3k | if (coalesce) parsed_str->enable_coalescing (); |
788 | 39.3k | } |
789 | 56.1k | current_parsed_str = parsed_str; |
790 | 56.1k | } |
791 | 56.3k | } void CFF::subr_subset_param_t::set_current_str<CFF::cff1_cs_interp_env_t>(CFF::cff1_cs_interp_env_t&, bool) Line | Count | Source | 770 | 8.24k | { | 771 | 8.24k | parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); | 772 | 8.24k | if (unlikely (!parsed_str)) | 773 | 0 | { | 774 | 0 | env.set_error (); | 775 | 0 | return; | 776 | 0 | } | 777 | | /* If the called subroutine is parsed partially but not completely yet, | 778 | | * it must be because we are calling it recursively. | 779 | | * Handle it as an error. */ | 780 | 8.24k | if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) | 781 | 137 | env.set_error (); | 782 | 8.10k | else | 783 | 8.10k | { | 784 | 8.10k | if (!parsed_str->is_parsed ()) | 785 | 5.89k | { | 786 | 5.89k | parsed_str->alloc (env.str_ref.total_size ()); | 787 | 5.89k | if (coalesce) parsed_str->enable_coalescing (); | 788 | 5.89k | } | 789 | 8.10k | current_parsed_str = parsed_str; | 790 | 8.10k | } | 791 | 8.24k | } |
void CFF::subr_subset_param_t::set_current_str<CFF::cff2_cs_interp_env_t<CFF::blend_arg_t> >(CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>&, bool) Line | Count | Source | 770 | 48.1k | { | 771 | 48.1k | parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); | 772 | 48.1k | if (unlikely (!parsed_str)) | 773 | 0 | { | 774 | 0 | env.set_error (); | 775 | 0 | return; | 776 | 0 | } | 777 | | /* If the called subroutine is parsed partially but not completely yet, | 778 | | * it must be because we are calling it recursively. | 779 | | * Handle it as an error. */ | 780 | 48.1k | if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) | 781 | 64 | env.set_error (); | 782 | 48.0k | else | 783 | 48.0k | { | 784 | 48.0k | if (!parsed_str->is_parsed ()) | 785 | 33.4k | { | 786 | 33.4k | parsed_str->alloc (env.str_ref.total_size ()); | 787 | 33.4k | if (coalesce) parsed_str->enable_coalescing (); | 788 | 33.4k | } | 789 | 48.0k | current_parsed_str = parsed_str; | 790 | 48.0k | } | 791 | 48.1k | } |
|
792 | | |
793 | | parsed_cs_str_t *current_parsed_str; |
794 | | |
795 | | parsed_cs_str_t *parsed_charstring; |
796 | | parsed_cs_str_vec_t *parsed_global_subrs; |
797 | | parsed_cs_str_vec_t *parsed_local_subrs; |
798 | | hb_set_t *global_closure; |
799 | | hb_set_t *local_closure; |
800 | | bool drop_hints; |
801 | | bool coalesce; |
802 | | }; |
803 | | |
804 | | struct subr_remap_t : hb_inc_bimap_t |
805 | | { |
806 | | void create (const hb_set_t *closure) |
807 | 16.8k | { |
808 | | /* create a remapping of subroutine numbers from old to new. |
809 | | * no optimization based on usage counts. fonttools doesn't appear doing that either. |
810 | | */ |
811 | | |
812 | 16.8k | alloc (closure->get_population ()); |
813 | 16.8k | for (auto old_num : *closure) |
814 | 13.3k | add (old_num); |
815 | | |
816 | 16.8k | if (get_population () < 1240) |
817 | 16.8k | bias = 107; |
818 | 0 | else if (get_population () < 33900) |
819 | 0 | bias = 1131; |
820 | 0 | else |
821 | 0 | bias = 32768; |
822 | 16.8k | } |
823 | | |
824 | | int biased_num (unsigned int old_num) const |
825 | 21.3k | { |
826 | 21.3k | hb_codepoint_t new_num = get (old_num); |
827 | 21.3k | return (int)new_num - bias; |
828 | 21.3k | } |
829 | | |
830 | | protected: |
831 | | int bias; |
832 | | }; |
833 | | |
834 | | struct subr_remaps_t |
835 | | { |
836 | | subr_remaps_t (unsigned int fdCount) |
837 | 6.10k | { |
838 | 6.10k | local_remaps.resize (fdCount); |
839 | 6.10k | } |
840 | | |
841 | | bool in_error() |
842 | 6.09k | { |
843 | 6.09k | return local_remaps.in_error (); |
844 | 6.09k | } |
845 | | |
846 | | void create (subr_closures_t& closures) |
847 | 5.07k | { |
848 | 5.07k | global_remap.create (&closures.global_closure); |
849 | 16.8k | for (unsigned int i = 0; i < local_remaps.length; i++) |
850 | 11.7k | local_remaps.arrayZ[i].create (&closures.local_closures[i]); |
851 | 5.07k | } |
852 | | |
853 | | subr_remap_t global_remap; |
854 | | hb_vector_t<subr_remap_t> local_remaps; |
855 | | }; |
856 | | |
857 | | template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid> |
858 | | struct subr_subsetter_t |
859 | | { |
860 | | subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) |
861 | 6.10k | : acc (acc_), plan (plan_), closures(acc_.fdCount), |
862 | 6.10k | remaps(acc_.fdCount) |
863 | 6.10k | {}CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::subr_subsetter_t(OT::cff1::accelerator_subset_t const&, hb_subset_plan_t const*) Line | Count | Source | 861 | 4.48k | : acc (acc_), plan (plan_), closures(acc_.fdCount), | 862 | 4.48k | remaps(acc_.fdCount) | 863 | 4.48k | {} |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::subr_subsetter_t(OT::cff2::accelerator_subset_t const&, hb_subset_plan_t const*) Line | Count | Source | 861 | 1.62k | : acc (acc_), plan (plan_), closures(acc_.fdCount), | 862 | 1.62k | remaps(acc_.fdCount) | 863 | 1.62k | {} |
|
864 | | |
865 | | /* Subroutine subsetting with --no-desubroutinize runs in phases: |
866 | | * |
867 | | * 1. execute charstrings/subroutines to determine subroutine closures |
868 | | * 2. parse out all operators and numbers |
869 | | * 3. mark hint operators and operands for removal if --no-hinting |
870 | | * 4. re-encode all charstrings and subroutines with new subroutine numbers |
871 | | * |
872 | | * Phases #1 and #2 are done at the same time in collect_subrs (). |
873 | | * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required), |
874 | | * because we can't tell if a number belongs to a hint op until we see the first moveto. |
875 | | * |
876 | | * Retained calls require an immediately preceding subroutine-number token |
877 | | * within the same program. Caller-supplied operands are rejected; use |
878 | | * desubroutinization for those programs. |
879 | | */ |
880 | | bool subset (void) |
881 | 6.10k | { |
882 | 6.10k | unsigned fd_count = acc.fdCount; |
883 | 6.10k | const cff_subset_accelerator_t* cff_accelerator = nullptr; |
884 | 6.10k | if (acc.cff_accelerator) { |
885 | 0 | cff_accelerator = acc.cff_accelerator; |
886 | 0 | fd_count = cff_accelerator->parsed_local_subrs.length; |
887 | 0 | } |
888 | | |
889 | 6.10k | if (cff_accelerator) { |
890 | | // If we are not dropping hinting then charstrings are not modified so we can |
891 | | // just use a reference to the cached copies. |
892 | 0 | cached_charstrings.resize_exact (plan->num_output_glyphs ()); |
893 | 0 | parsed_global_subrs = &cff_accelerator->parsed_global_subrs; |
894 | 0 | parsed_local_subrs = &cff_accelerator->parsed_local_subrs; |
895 | 6.10k | } else { |
896 | | /* Font DICTs can share a Private DICT and its Subrs, so the size of |
897 | | * the table does not bound the sum of their counts. Every subroutine |
898 | | * of a font that does not share them takes at least one byte of the |
899 | | * table; _hb_subset_table_try() would not serialize more than this |
900 | | * anyway. Over the limit the subset fails, as any CFF planning |
901 | | * failure has since dde7da182e. */ |
902 | 6.10k | uint64_t num_subrs = acc.globalSubrs->count; |
903 | 20.1k | for (unsigned int i = 0; i < acc.fdCount; i++) |
904 | 14.0k | num_subrs += acc.privateDicts[i].localSubrs->count; |
905 | 6.10k | if (unlikely (num_subrs * sizeof (parsed_cs_str_t) > |
906 | 6.10k | (uint64_t) acc.get_blob ()->length * 256)) |
907 | 0 | return false; |
908 | | |
909 | 6.10k | parsed_charstrings.resize_exact (plan->num_output_glyphs ()); |
910 | 6.10k | parsed_global_subrs_storage.resize_exact (acc.globalSubrs->count); |
911 | | |
912 | 6.10k | if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false; |
913 | | |
914 | 20.1k | for (unsigned int i = 0; i < acc.fdCount; i++) |
915 | 14.0k | { |
916 | 14.0k | unsigned count = acc.privateDicts[i].localSubrs->count; |
917 | 14.0k | parsed_local_subrs_storage[i].resize (count); |
918 | 14.0k | if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false; |
919 | 14.0k | } |
920 | | |
921 | 6.09k | parsed_global_subrs = &parsed_global_subrs_storage; |
922 | 6.09k | parsed_local_subrs = &parsed_local_subrs_storage; |
923 | 6.09k | } |
924 | | |
925 | 6.09k | if (unlikely (remaps.in_error() |
926 | 6.09k | || cached_charstrings.in_error () |
927 | 6.09k | || parsed_charstrings.in_error () |
928 | 6.09k | || parsed_global_subrs->in_error () |
929 | 6.09k | || closures.in_error ())) { |
930 | 9 | return false; |
931 | 9 | } |
932 | | |
933 | | /* When hints are not analyzed (not dropping hints, not populating |
934 | | * the accelerator), coalesce parsed tokens as they are added, |
935 | | * instead of a separate compact() pass. */ |
936 | 6.09k | bool coalesce = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING) && |
937 | 5.05k | !plan->inprogress_accelerator; |
938 | | |
939 | 6.09k | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); |
940 | | |
941 | | /* phase 1 & 2 */ |
942 | 6.09k | for (auto _ : plan->new_to_old_gid_list) |
943 | 13.5k | { |
944 | 13.5k | hb_codepoint_t new_glyph = _.first; |
945 | 13.5k | hb_codepoint_t old_glyph = _.second; |
946 | | |
947 | 13.5k | const hb_ubytes_t str = (*acc.charStrings)[old_glyph]; |
948 | 13.5k | unsigned int fd = subset_get_fd (acc, old_glyph); |
949 | 13.5k | if (unlikely (fd >= acc.fdCount)) |
950 | 49 | return false; |
951 | | |
952 | 13.4k | if (cff_accelerator) |
953 | 0 | { |
954 | | // parsed string already exists in accelerator, copy it and move |
955 | | // on. |
956 | 0 | if (cached_charstrings) |
957 | 0 | cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph]; |
958 | 0 | else |
959 | 0 | parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph]; |
960 | |
|
961 | 0 | continue; |
962 | 0 | } |
963 | | |
964 | 13.4k | ENV env (str, acc, fd); |
965 | 13.4k | cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env); |
966 | | |
967 | 13.4k | parsed_charstrings[new_glyph].alloc (str.length); |
968 | 13.4k | subr_subset_param_t param (&parsed_charstrings[new_glyph], |
969 | 13.4k | &parsed_global_subrs_storage, |
970 | 13.4k | &parsed_local_subrs_storage[fd], |
971 | 13.4k | &closures.global_closure, |
972 | 13.4k | &closures.local_closures[fd], |
973 | 13.4k | plan->flags & HB_SUBSET_FLAGS_NO_HINTING, |
974 | 13.4k | coalesce); |
975 | | |
976 | 13.4k | if (unlikely (!interp.interpret (param, budget))) |
977 | 964 | return false; |
978 | | |
979 | | /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ |
980 | 12.5k | SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]); |
981 | | |
982 | | /* mark hint ops and arguments for drop */ |
983 | 12.5k | if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator) |
984 | 1.40k | { |
985 | 1.40k | subr_subset_param_t param (&parsed_charstrings[new_glyph], |
986 | 1.40k | &parsed_global_subrs_storage, |
987 | 1.40k | &parsed_local_subrs_storage[fd], |
988 | 1.40k | &closures.global_closure, |
989 | 1.40k | &closures.local_closures[fd], |
990 | 1.40k | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); |
991 | | |
992 | 1.40k | drop_hints_param_t drop (budget); |
993 | 1.40k | if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop)) |
994 | 831 | { |
995 | 831 | parsed_charstrings[new_glyph].set_hint_dropped (); |
996 | 831 | if (drop.vsindex_dropped) |
997 | 48 | parsed_charstrings[new_glyph].set_vsindex_dropped (); |
998 | 831 | } |
999 | 1.40k | if (unlikely (drop.in_error ())) |
1000 | 0 | return false; |
1001 | 1.40k | } |
1002 | | |
1003 | | /* Doing this here one by one instead of compacting all at the end |
1004 | | * has massive peak-memory saving. |
1005 | | * |
1006 | | * The compacting both saves memory and makes further operations |
1007 | | * faster. |
1008 | | * |
1009 | | * Not needed when tokens were coalesced during parsing. |
1010 | | */ |
1011 | 12.5k | if (!coalesce) |
1012 | 1.40k | parsed_charstrings[new_glyph].compact (); |
1013 | 12.5k | } |
1014 | | |
1015 | | /* Since parsed strings were loaded from accelerator, we still need |
1016 | | * to compute the subroutine closures which would have normally happened during |
1017 | | * parsing. |
1018 | | * |
1019 | | * Or if we are dropping hinting, redo closure to get actually used subrs. |
1020 | | */ |
1021 | 5.07k | if ((cff_accelerator || |
1022 | 5.07k | (!cff_accelerator && plan->flags & HB_SUBSET_FLAGS_NO_HINTING)) && |
1023 | 948 | !closure_subroutines(*parsed_global_subrs, |
1024 | 948 | *parsed_local_subrs)) |
1025 | 0 | return false; |
1026 | | |
1027 | 5.07k | remaps.create (closures); |
1028 | | |
1029 | 5.07k | populate_subset_accelerator (); |
1030 | 5.07k | return true; |
1031 | 5.07k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::subset() Line | Count | Source | 881 | 4.48k | { | 882 | 4.48k | unsigned fd_count = acc.fdCount; | 883 | 4.48k | const cff_subset_accelerator_t* cff_accelerator = nullptr; | 884 | 4.48k | if (acc.cff_accelerator) { | 885 | 0 | cff_accelerator = acc.cff_accelerator; | 886 | 0 | fd_count = cff_accelerator->parsed_local_subrs.length; | 887 | 0 | } | 888 | | | 889 | 4.48k | if (cff_accelerator) { | 890 | | // If we are not dropping hinting then charstrings are not modified so we can | 891 | | // just use a reference to the cached copies. | 892 | 0 | cached_charstrings.resize_exact (plan->num_output_glyphs ()); | 893 | 0 | parsed_global_subrs = &cff_accelerator->parsed_global_subrs; | 894 | 0 | parsed_local_subrs = &cff_accelerator->parsed_local_subrs; | 895 | 4.48k | } else { | 896 | | /* Font DICTs can share a Private DICT and its Subrs, so the size of | 897 | | * the table does not bound the sum of their counts. Every subroutine | 898 | | * of a font that does not share them takes at least one byte of the | 899 | | * table; _hb_subset_table_try() would not serialize more than this | 900 | | * anyway. Over the limit the subset fails, as any CFF planning | 901 | | * failure has since dde7da182e. */ | 902 | 4.48k | uint64_t num_subrs = acc.globalSubrs->count; | 903 | 15.2k | for (unsigned int i = 0; i < acc.fdCount; i++) | 904 | 10.7k | num_subrs += acc.privateDicts[i].localSubrs->count; | 905 | 4.48k | if (unlikely (num_subrs * sizeof (parsed_cs_str_t) > | 906 | 4.48k | (uint64_t) acc.get_blob ()->length * 256)) | 907 | 0 | return false; | 908 | | | 909 | 4.48k | parsed_charstrings.resize_exact (plan->num_output_glyphs ()); | 910 | 4.48k | parsed_global_subrs_storage.resize_exact (acc.globalSubrs->count); | 911 | | | 912 | 4.48k | if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false; | 913 | | | 914 | 15.1k | for (unsigned int i = 0; i < acc.fdCount; i++) | 915 | 10.7k | { | 916 | 10.7k | unsigned count = acc.privateDicts[i].localSubrs->count; | 917 | 10.7k | parsed_local_subrs_storage[i].resize (count); | 918 | 10.7k | if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false; | 919 | 10.7k | } | 920 | | | 921 | 4.48k | parsed_global_subrs = &parsed_global_subrs_storage; | 922 | 4.48k | parsed_local_subrs = &parsed_local_subrs_storage; | 923 | 4.48k | } | 924 | | | 925 | 4.48k | if (unlikely (remaps.in_error() | 926 | 4.48k | || cached_charstrings.in_error () | 927 | 4.48k | || parsed_charstrings.in_error () | 928 | 4.48k | || parsed_global_subrs->in_error () | 929 | 4.48k | || closures.in_error ())) { | 930 | 5 | return false; | 931 | 5 | } | 932 | | | 933 | | /* When hints are not analyzed (not dropping hints, not populating | 934 | | * the accelerator), coalesce parsed tokens as they are added, | 935 | | * instead of a separate compact() pass. */ | 936 | 4.47k | bool coalesce = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING) && | 937 | 3.69k | !plan->inprogress_accelerator; | 938 | | | 939 | 4.47k | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); | 940 | | | 941 | | /* phase 1 & 2 */ | 942 | 4.47k | for (auto _ : plan->new_to_old_gid_list) | 943 | 9.19k | { | 944 | 9.19k | hb_codepoint_t new_glyph = _.first; | 945 | 9.19k | hb_codepoint_t old_glyph = _.second; | 946 | | | 947 | 9.19k | const hb_ubytes_t str = (*acc.charStrings)[old_glyph]; | 948 | 9.19k | unsigned int fd = subset_get_fd (acc, old_glyph); | 949 | 9.19k | if (unlikely (fd >= acc.fdCount)) | 950 | 23 | return false; | 951 | | | 952 | 9.16k | if (cff_accelerator) | 953 | 0 | { | 954 | | // parsed string already exists in accelerator, copy it and move | 955 | | // on. | 956 | 0 | if (cached_charstrings) | 957 | 0 | cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph]; | 958 | 0 | else | 959 | 0 | parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph]; | 960 | |
| 961 | 0 | continue; | 962 | 0 | } | 963 | | | 964 | 9.16k | ENV env (str, acc, fd); | 965 | 9.16k | cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env); | 966 | | | 967 | 9.16k | parsed_charstrings[new_glyph].alloc (str.length); | 968 | 9.16k | subr_subset_param_t param (&parsed_charstrings[new_glyph], | 969 | 9.16k | &parsed_global_subrs_storage, | 970 | 9.16k | &parsed_local_subrs_storage[fd], | 971 | 9.16k | &closures.global_closure, | 972 | 9.16k | &closures.local_closures[fd], | 973 | 9.16k | plan->flags & HB_SUBSET_FLAGS_NO_HINTING, | 974 | 9.16k | coalesce); | 975 | | | 976 | 9.16k | if (unlikely (!interp.interpret (param, budget))) | 977 | 633 | return false; | 978 | | | 979 | | /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ | 980 | 8.53k | SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]); | 981 | | | 982 | | /* mark hint ops and arguments for drop */ | 983 | 8.53k | if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator) | 984 | 992 | { | 985 | 992 | subr_subset_param_t param (&parsed_charstrings[new_glyph], | 986 | 992 | &parsed_global_subrs_storage, | 987 | 992 | &parsed_local_subrs_storage[fd], | 988 | 992 | &closures.global_closure, | 989 | 992 | &closures.local_closures[fd], | 990 | 992 | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 991 | | | 992 | 992 | drop_hints_param_t drop (budget); | 993 | 992 | if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop)) | 994 | 684 | { | 995 | 684 | parsed_charstrings[new_glyph].set_hint_dropped (); | 996 | 684 | if (drop.vsindex_dropped) | 997 | 44 | parsed_charstrings[new_glyph].set_vsindex_dropped (); | 998 | 684 | } | 999 | 992 | if (unlikely (drop.in_error ())) | 1000 | 0 | return false; | 1001 | 992 | } | 1002 | | | 1003 | | /* Doing this here one by one instead of compacting all at the end | 1004 | | * has massive peak-memory saving. | 1005 | | * | 1006 | | * The compacting both saves memory and makes further operations | 1007 | | * faster. | 1008 | | * | 1009 | | * Not needed when tokens were coalesced during parsing. | 1010 | | */ | 1011 | 8.53k | if (!coalesce) | 1012 | 992 | parsed_charstrings[new_glyph].compact (); | 1013 | 8.53k | } | 1014 | | | 1015 | | /* Since parsed strings were loaded from accelerator, we still need | 1016 | | * to compute the subroutine closures which would have normally happened during | 1017 | | * parsing. | 1018 | | * | 1019 | | * Or if we are dropping hinting, redo closure to get actually used subrs. | 1020 | | */ | 1021 | 3.81k | if ((cff_accelerator || | 1022 | 3.81k | (!cff_accelerator && plan->flags & HB_SUBSET_FLAGS_NO_HINTING)) && | 1023 | 711 | !closure_subroutines(*parsed_global_subrs, | 1024 | 711 | *parsed_local_subrs)) | 1025 | 0 | return false; | 1026 | | | 1027 | 3.81k | remaps.create (closures); | 1028 | | | 1029 | 3.81k | populate_subset_accelerator (); | 1030 | 3.81k | return true; | 1031 | 3.81k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::subset() Line | Count | Source | 881 | 1.62k | { | 882 | 1.62k | unsigned fd_count = acc.fdCount; | 883 | 1.62k | const cff_subset_accelerator_t* cff_accelerator = nullptr; | 884 | 1.62k | if (acc.cff_accelerator) { | 885 | 0 | cff_accelerator = acc.cff_accelerator; | 886 | 0 | fd_count = cff_accelerator->parsed_local_subrs.length; | 887 | 0 | } | 888 | | | 889 | 1.62k | if (cff_accelerator) { | 890 | | // If we are not dropping hinting then charstrings are not modified so we can | 891 | | // just use a reference to the cached copies. | 892 | 0 | cached_charstrings.resize_exact (plan->num_output_glyphs ()); | 893 | 0 | parsed_global_subrs = &cff_accelerator->parsed_global_subrs; | 894 | 0 | parsed_local_subrs = &cff_accelerator->parsed_local_subrs; | 895 | 1.62k | } else { | 896 | | /* Font DICTs can share a Private DICT and its Subrs, so the size of | 897 | | * the table does not bound the sum of their counts. Every subroutine | 898 | | * of a font that does not share them takes at least one byte of the | 899 | | * table; _hb_subset_table_try() would not serialize more than this | 900 | | * anyway. Over the limit the subset fails, as any CFF planning | 901 | | * failure has since dde7da182e. */ | 902 | 1.62k | uint64_t num_subrs = acc.globalSubrs->count; | 903 | 4.92k | for (unsigned int i = 0; i < acc.fdCount; i++) | 904 | 3.30k | num_subrs += acc.privateDicts[i].localSubrs->count; | 905 | 1.62k | if (unlikely (num_subrs * sizeof (parsed_cs_str_t) > | 906 | 1.62k | (uint64_t) acc.get_blob ()->length * 256)) | 907 | 0 | return false; | 908 | | | 909 | 1.62k | parsed_charstrings.resize_exact (plan->num_output_glyphs ()); | 910 | 1.62k | parsed_global_subrs_storage.resize_exact (acc.globalSubrs->count); | 911 | | | 912 | 1.62k | if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false; | 913 | | | 914 | 4.92k | for (unsigned int i = 0; i < acc.fdCount; i++) | 915 | 3.30k | { | 916 | 3.30k | unsigned count = acc.privateDicts[i].localSubrs->count; | 917 | 3.30k | parsed_local_subrs_storage[i].resize (count); | 918 | 3.30k | if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false; | 919 | 3.30k | } | 920 | | | 921 | 1.61k | parsed_global_subrs = &parsed_global_subrs_storage; | 922 | 1.61k | parsed_local_subrs = &parsed_local_subrs_storage; | 923 | 1.61k | } | 924 | | | 925 | 1.61k | if (unlikely (remaps.in_error() | 926 | 1.61k | || cached_charstrings.in_error () | 927 | 1.61k | || parsed_charstrings.in_error () | 928 | 1.61k | || parsed_global_subrs->in_error () | 929 | 1.61k | || closures.in_error ())) { | 930 | 4 | return false; | 931 | 4 | } | 932 | | | 933 | | /* When hints are not analyzed (not dropping hints, not populating | 934 | | * the accelerator), coalesce parsed tokens as they are added, | 935 | | * instead of a separate compact() pass. */ | 936 | 1.61k | bool coalesce = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING) && | 937 | 1.36k | !plan->inprogress_accelerator; | 938 | | | 939 | 1.61k | int64_t *budget = plan->cff_budget (get_subset_budget (acc.get_blob ())); | 940 | | | 941 | | /* phase 1 & 2 */ | 942 | 1.61k | for (auto _ : plan->new_to_old_gid_list) | 943 | 4.34k | { | 944 | 4.34k | hb_codepoint_t new_glyph = _.first; | 945 | 4.34k | hb_codepoint_t old_glyph = _.second; | 946 | | | 947 | 4.34k | const hb_ubytes_t str = (*acc.charStrings)[old_glyph]; | 948 | 4.34k | unsigned int fd = subset_get_fd (acc, old_glyph); | 949 | 4.34k | if (unlikely (fd >= acc.fdCount)) | 950 | 26 | return false; | 951 | | | 952 | 4.32k | if (cff_accelerator) | 953 | 0 | { | 954 | | // parsed string already exists in accelerator, copy it and move | 955 | | // on. | 956 | 0 | if (cached_charstrings) | 957 | 0 | cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph]; | 958 | 0 | else | 959 | 0 | parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph]; | 960 | |
| 961 | 0 | continue; | 962 | 0 | } | 963 | | | 964 | 4.32k | ENV env (str, acc, fd); | 965 | 4.32k | cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env); | 966 | | | 967 | 4.32k | parsed_charstrings[new_glyph].alloc (str.length); | 968 | 4.32k | subr_subset_param_t param (&parsed_charstrings[new_glyph], | 969 | 4.32k | &parsed_global_subrs_storage, | 970 | 4.32k | &parsed_local_subrs_storage[fd], | 971 | 4.32k | &closures.global_closure, | 972 | 4.32k | &closures.local_closures[fd], | 973 | 4.32k | plan->flags & HB_SUBSET_FLAGS_NO_HINTING, | 974 | 4.32k | coalesce); | 975 | | | 976 | 4.32k | if (unlikely (!interp.interpret (param, budget))) | 977 | 331 | return false; | 978 | | | 979 | | /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ | 980 | 3.99k | SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]); | 981 | | | 982 | | /* mark hint ops and arguments for drop */ | 983 | 3.99k | if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator) | 984 | 417 | { | 985 | 417 | subr_subset_param_t param (&parsed_charstrings[new_glyph], | 986 | 417 | &parsed_global_subrs_storage, | 987 | 417 | &parsed_local_subrs_storage[fd], | 988 | 417 | &closures.global_closure, | 989 | 417 | &closures.local_closures[fd], | 990 | 417 | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 991 | | | 992 | 417 | drop_hints_param_t drop (budget); | 993 | 417 | if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop)) | 994 | 147 | { | 995 | 147 | parsed_charstrings[new_glyph].set_hint_dropped (); | 996 | 147 | if (drop.vsindex_dropped) | 997 | 4 | parsed_charstrings[new_glyph].set_vsindex_dropped (); | 998 | 147 | } | 999 | 417 | if (unlikely (drop.in_error ())) | 1000 | 0 | return false; | 1001 | 417 | } | 1002 | | | 1003 | | /* Doing this here one by one instead of compacting all at the end | 1004 | | * has massive peak-memory saving. | 1005 | | * | 1006 | | * The compacting both saves memory and makes further operations | 1007 | | * faster. | 1008 | | * | 1009 | | * Not needed when tokens were coalesced during parsing. | 1010 | | */ | 1011 | 3.99k | if (!coalesce) | 1012 | 417 | parsed_charstrings[new_glyph].compact (); | 1013 | 3.99k | } | 1014 | | | 1015 | | /* Since parsed strings were loaded from accelerator, we still need | 1016 | | * to compute the subroutine closures which would have normally happened during | 1017 | | * parsing. | 1018 | | * | 1019 | | * Or if we are dropping hinting, redo closure to get actually used subrs. | 1020 | | */ | 1021 | 1.25k | if ((cff_accelerator || | 1022 | 1.25k | (!cff_accelerator && plan->flags & HB_SUBSET_FLAGS_NO_HINTING)) && | 1023 | 237 | !closure_subroutines(*parsed_global_subrs, | 1024 | 237 | *parsed_local_subrs)) | 1025 | 0 | return false; | 1026 | | | 1027 | 1.25k | remaps.create (closures); | 1028 | | | 1029 | 1.25k | populate_subset_accelerator (); | 1030 | 1.25k | return true; | 1031 | 1.25k | } |
|
1032 | | |
1033 | | bool encode_charstrings (str_buff_vec_t &buffArray, bool encode_prefix = true) const |
1034 | 5.06k | { |
1035 | 5.06k | unsigned num_glyphs = plan->num_output_glyphs (); |
1036 | 5.06k | if (unlikely (!buffArray.resize_exact (num_glyphs))) |
1037 | 5 | return false; |
1038 | 5.05k | hb_codepoint_t last = 0; |
1039 | 5.05k | for (auto _ : plan->new_to_old_gid_list) |
1040 | 11.7k | { |
1041 | 11.7k | hb_codepoint_t gid = _.first; |
1042 | 11.7k | hb_codepoint_t old_glyph = _.second; |
1043 | | |
1044 | 11.7k | if (endchar_op != OpCode_Invalid) |
1045 | 10.4k | for (; last < gid; last++) |
1046 | 2.29k | { |
1047 | | // Hack to point vector to static string. |
1048 | 2.29k | auto &b = buffArray.arrayZ[last]; |
1049 | 2.29k | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); |
1050 | 2.29k | } |
1051 | | |
1052 | 11.7k | last++; // Skip over gid |
1053 | 11.7k | unsigned int fd = subset_get_fd (acc, old_glyph); |
1054 | 11.7k | if (unlikely (fd >= acc.fdCount)) |
1055 | 0 | return false; |
1056 | 11.7k | if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix))) |
1057 | 2 | return false; |
1058 | 11.7k | } |
1059 | 5.05k | if (endchar_op != OpCode_Invalid) |
1060 | 17.6k | for (; last < num_glyphs; last++) |
1061 | 13.8k | { |
1062 | | // Hack to point vector to static string. |
1063 | 13.8k | auto &b = buffArray.arrayZ[last]; |
1064 | 13.8k | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); |
1065 | 13.8k | } |
1066 | | |
1067 | 5.05k | return true; |
1068 | 5.05k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::encode_charstrings(hb_vector_t<hb_vector_t<unsigned char, false>, false>&, bool) const Line | Count | Source | 1034 | 3.80k | { | 1035 | 3.80k | unsigned num_glyphs = plan->num_output_glyphs (); | 1036 | 3.80k | if (unlikely (!buffArray.resize_exact (num_glyphs))) | 1037 | 2 | return false; | 1038 | 3.80k | hb_codepoint_t last = 0; | 1039 | 3.80k | for (auto _ : plan->new_to_old_gid_list) | 1040 | 8.17k | { | 1041 | 8.17k | hb_codepoint_t gid = _.first; | 1042 | 8.17k | hb_codepoint_t old_glyph = _.second; | 1043 | | | 1044 | 8.17k | if (endchar_op != OpCode_Invalid) | 1045 | 10.4k | for (; last < gid; last++) | 1046 | 2.29k | { | 1047 | | // Hack to point vector to static string. | 1048 | 2.29k | auto &b = buffArray.arrayZ[last]; | 1049 | 2.29k | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); | 1050 | 2.29k | } | 1051 | | | 1052 | 8.17k | last++; // Skip over gid | 1053 | 8.17k | unsigned int fd = subset_get_fd (acc, old_glyph); | 1054 | 8.17k | if (unlikely (fd >= acc.fdCount)) | 1055 | 0 | return false; | 1056 | 8.17k | if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix))) | 1057 | 1 | return false; | 1058 | 8.17k | } | 1059 | 3.80k | if (endchar_op != OpCode_Invalid) | 1060 | 17.6k | for (; last < num_glyphs; last++) | 1061 | 13.8k | { | 1062 | | // Hack to point vector to static string. | 1063 | 13.8k | auto &b = buffArray.arrayZ[last]; | 1064 | 13.8k | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); | 1065 | 13.8k | } | 1066 | | | 1067 | 3.80k | return true; | 1068 | 3.80k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::encode_charstrings(hb_vector_t<hb_vector_t<unsigned char, false>, false>&, bool) const Line | Count | Source | 1034 | 1.25k | { | 1035 | 1.25k | unsigned num_glyphs = plan->num_output_glyphs (); | 1036 | 1.25k | if (unlikely (!buffArray.resize_exact (num_glyphs))) | 1037 | 3 | return false; | 1038 | 1.25k | hb_codepoint_t last = 0; | 1039 | 1.25k | for (auto _ : plan->new_to_old_gid_list) | 1040 | 3.60k | { | 1041 | 3.60k | hb_codepoint_t gid = _.first; | 1042 | 3.60k | hb_codepoint_t old_glyph = _.second; | 1043 | | | 1044 | 3.60k | if (endchar_op != OpCode_Invalid) | 1045 | 0 | for (; last < gid; last++) | 1046 | 0 | { | 1047 | | // Hack to point vector to static string. | 1048 | 0 | auto &b = buffArray.arrayZ[last]; | 1049 | 0 | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); | 1050 | 0 | } | 1051 | | | 1052 | 3.60k | last++; // Skip over gid | 1053 | 3.60k | unsigned int fd = subset_get_fd (acc, old_glyph); | 1054 | 3.60k | if (unlikely (fd >= acc.fdCount)) | 1055 | 0 | return false; | 1056 | 3.60k | if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix))) | 1057 | 1 | return false; | 1058 | 3.60k | } | 1059 | 1.25k | if (endchar_op != OpCode_Invalid) | 1060 | 0 | for (; last < num_glyphs; last++) | 1061 | 0 | { | 1062 | | // Hack to point vector to static string. | 1063 | 0 | auto &b = buffArray.arrayZ[last]; | 1064 | 0 | b.set_storage (const_cast<unsigned char *>(endchar_str), 1); | 1065 | 0 | } | 1066 | | | 1067 | 1.25k | return true; | 1068 | 1.25k | } |
|
1069 | | |
1070 | | bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const |
1071 | 11.5k | { |
1072 | 11.5k | unsigned int count = remap.get_population (); |
1073 | | |
1074 | 11.5k | if (unlikely (!buffArray.resize_exact (count))) |
1075 | 0 | return false; |
1076 | 24.8k | for (unsigned int new_num = 0; new_num < count; new_num++) |
1077 | 13.3k | { |
1078 | 13.3k | hb_codepoint_t old_num = remap.backward (new_num); |
1079 | 13.3k | assert (old_num != CFF_UNDEF_CODE); |
1080 | | |
1081 | 13.3k | if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) |
1082 | 1 | return false; |
1083 | 13.3k | } |
1084 | 11.5k | return true; |
1085 | 11.5k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::encode_subrs(CFF::parsed_cs_str_vec_t const&, CFF::subr_remap_t const&, unsigned int, hb_vector_t<hb_vector_t<unsigned char, false>, false>&) const Line | Count | Source | 1071 | 7.78k | { | 1072 | 7.78k | unsigned int count = remap.get_population (); | 1073 | | | 1074 | 7.78k | if (unlikely (!buffArray.resize_exact (count))) | 1075 | 0 | return false; | 1076 | 9.77k | for (unsigned int new_num = 0; new_num < count; new_num++) | 1077 | 1.98k | { | 1078 | 1.98k | hb_codepoint_t old_num = remap.backward (new_num); | 1079 | 1.98k | assert (old_num != CFF_UNDEF_CODE); | 1080 | | | 1081 | 1.98k | if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) | 1082 | 0 | return false; | 1083 | 1.98k | } | 1084 | 7.78k | return true; | 1085 | 7.78k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::encode_subrs(CFF::parsed_cs_str_vec_t const&, CFF::subr_remap_t const&, unsigned int, hb_vector_t<hb_vector_t<unsigned char, false>, false>&) const Line | Count | Source | 1071 | 3.74k | { | 1072 | 3.74k | unsigned int count = remap.get_population (); | 1073 | | | 1074 | 3.74k | if (unlikely (!buffArray.resize_exact (count))) | 1075 | 0 | return false; | 1076 | 15.0k | for (unsigned int new_num = 0; new_num < count; new_num++) | 1077 | 11.3k | { | 1078 | 11.3k | hb_codepoint_t old_num = remap.backward (new_num); | 1079 | 11.3k | assert (old_num != CFF_UNDEF_CODE); | 1080 | | | 1081 | 11.3k | if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) | 1082 | 1 | return false; | 1083 | 11.3k | } | 1084 | 3.74k | return true; | 1085 | 3.74k | } |
|
1086 | | |
1087 | | bool encode_globalsubrs (str_buff_vec_t &buffArray) |
1088 | 5.05k | { |
1089 | 5.05k | return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray); |
1090 | 5.05k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::encode_globalsubrs(hb_vector_t<hb_vector_t<unsigned char, false>, false>&) Line | Count | Source | 1088 | 3.80k | { | 1089 | 3.80k | return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray); | 1090 | 3.80k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::encode_globalsubrs(hb_vector_t<hb_vector_t<unsigned char, false>, false>&) Line | Count | Source | 1088 | 1.25k | { | 1089 | 1.25k | return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray); | 1090 | 1.25k | } |
|
1091 | | |
1092 | | bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const |
1093 | 6.47k | { |
1094 | 6.47k | return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray); |
1095 | 6.47k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::encode_localsubrs(unsigned int, hb_vector_t<hb_vector_t<unsigned char, false>, false>&) const Line | Count | Source | 1093 | 3.98k | { | 1094 | 3.98k | return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray); | 1095 | 3.98k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::encode_localsubrs(unsigned int, hb_vector_t<hb_vector_t<unsigned char, false>, false>&) const Line | Count | Source | 1093 | 2.48k | { | 1094 | 2.48k | return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray); | 1095 | 2.48k | } |
|
1096 | | |
1097 | | bool has_identity_global_remap () const |
1098 | 39 | { |
1099 | 39 | return remaps.global_remap.get_population () == acc.globalSubrs->count; |
1100 | 39 | } |
1101 | | |
1102 | | bool has_identity_local_remap (unsigned int fd) const |
1103 | 31 | { |
1104 | 31 | return remaps.local_remaps[fd].get_population () == |
1105 | 31 | acc.privateDicts[fd].localSubrs->count; |
1106 | 31 | } |
1107 | | |
1108 | | protected: |
1109 | | /* The walks over parsed strings below need not follow the path the |
1110 | | * interpreter took for the glyph at hand: a string is parsed on its first |
1111 | | * run only, global subrs are shared by font dicts with different local |
1112 | | * subrs, and endchar does not end a walk. So the recorded call graph may |
1113 | | * nest deeper than the interpreter allows, or even be cyclic, and the walks |
1114 | | * enforce the interpreter's call-depth limit themselves: a subr called |
1115 | | * past that depth is not walked into, as best effort, and the subset |
1116 | | * goes on. The hint-dropping walk also charges its work to the |
1117 | | * subsetting pass budget. */ |
1118 | | struct drop_hints_param_t |
1119 | | { |
1120 | | drop_hints_param_t (int64_t *budget_) |
1121 | 1.40k | : seen_moveto (false), |
1122 | 1.40k | ends_in_hint (false), |
1123 | 1.40k | all_dropped (false), |
1124 | 1.40k | vsindex_dropped (false), |
1125 | 1.40k | budget (budget_) {}CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_param_t::drop_hints_param_t(long*) Line | Count | Source | 1121 | 992 | : seen_moveto (false), | 1122 | 992 | ends_in_hint (false), | 1123 | 992 | all_dropped (false), | 1124 | 992 | vsindex_dropped (false), | 1125 | 992 | budget (budget_) {} |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_param_t::drop_hints_param_t(long*) Line | Count | Source | 1121 | 417 | : seen_moveto (false), | 1122 | 417 | ends_in_hint (false), | 1123 | 417 | all_dropped (false), | 1124 | 417 | vsindex_dropped (false), | 1125 | 417 | budget (budget_) {} |
|
1126 | | |
1127 | 1.40k | bool in_error () const { return *budget < 0; }CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_param_t::in_error() const Line | Count | Source | 1127 | 992 | bool in_error () const { return *budget < 0; } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_param_t::in_error() const Line | Count | Source | 1127 | 417 | bool in_error () const { return *budget < 0; } |
|
1128 | | |
1129 | | bool seen_moveto; |
1130 | | bool ends_in_hint; |
1131 | | bool all_dropped; |
1132 | | bool vsindex_dropped; |
1133 | | unsigned depth = 0; |
1134 | | int64_t *budget; |
1135 | | }; |
1136 | | |
1137 | | bool drop_hints_in_subr (parsed_cs_str_t &str, unsigned int pos, |
1138 | | parsed_cs_str_vec_t &subrs, unsigned int subr_num, |
1139 | | const subr_subset_param_t ¶m, drop_hints_param_t &drop) |
1140 | 2.83k | { |
1141 | 2.83k | drop.ends_in_hint = false; |
1142 | 2.83k | if (unlikely (drop.depth >= kMaxCallLimit)) |
1143 | 0 | { |
1144 | | /* Too deep to walk into; keep the call and its hints. */ |
1145 | 0 | drop.all_dropped = false; |
1146 | 0 | return false; |
1147 | 0 | } |
1148 | 2.83k | drop.depth++; |
1149 | 2.83k | bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); |
1150 | 2.83k | drop.depth--; |
1151 | | |
1152 | | /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), |
1153 | | * then this entire subroutine must be a hint. drop its call. */ |
1154 | 2.83k | if (drop.ends_in_hint) |
1155 | 103 | { |
1156 | 103 | str.values[pos].set_hinting (); |
1157 | | /* if this subr call is at the end of the parent subr, propagate the flag |
1158 | | * otherwise reset the flag */ |
1159 | 103 | if (!str.at_end (pos)) |
1160 | 76 | drop.ends_in_hint = false; |
1161 | 103 | } |
1162 | 2.73k | else if (drop.all_dropped) |
1163 | 364 | { |
1164 | 364 | str.values[pos].set_hinting (); |
1165 | 364 | } |
1166 | | |
1167 | 2.83k | return has_hint; |
1168 | 2.83k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_in_subr(CFF::parsed_cs_str_t&, unsigned int, CFF::parsed_cs_str_vec_t&, unsigned int, CFF::subr_subset_param_t const&, CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_param_t&) Line | Count | Source | 1140 | 555 | { | 1141 | 555 | drop.ends_in_hint = false; | 1142 | 555 | if (unlikely (drop.depth >= kMaxCallLimit)) | 1143 | 0 | { | 1144 | | /* Too deep to walk into; keep the call and its hints. */ | 1145 | 0 | drop.all_dropped = false; | 1146 | 0 | return false; | 1147 | 0 | } | 1148 | 555 | drop.depth++; | 1149 | 555 | bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); | 1150 | 555 | drop.depth--; | 1151 | | | 1152 | | /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), | 1153 | | * then this entire subroutine must be a hint. drop its call. */ | 1154 | 555 | if (drop.ends_in_hint) | 1155 | 43 | { | 1156 | 43 | str.values[pos].set_hinting (); | 1157 | | /* if this subr call is at the end of the parent subr, propagate the flag | 1158 | | * otherwise reset the flag */ | 1159 | 43 | if (!str.at_end (pos)) | 1160 | 33 | drop.ends_in_hint = false; | 1161 | 43 | } | 1162 | 512 | else if (drop.all_dropped) | 1163 | 33 | { | 1164 | 33 | str.values[pos].set_hinting (); | 1165 | 33 | } | 1166 | | | 1167 | 555 | return has_hint; | 1168 | 555 | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_in_subr(CFF::parsed_cs_str_t&, unsigned int, CFF::parsed_cs_str_vec_t&, unsigned int, CFF::subr_subset_param_t const&, CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_param_t&) Line | Count | Source | 1140 | 2.27k | { | 1141 | 2.27k | drop.ends_in_hint = false; | 1142 | 2.27k | if (unlikely (drop.depth >= kMaxCallLimit)) | 1143 | 0 | { | 1144 | | /* Too deep to walk into; keep the call and its hints. */ | 1145 | 0 | drop.all_dropped = false; | 1146 | 0 | return false; | 1147 | 0 | } | 1148 | 2.27k | drop.depth++; | 1149 | 2.27k | bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); | 1150 | 2.27k | drop.depth--; | 1151 | | | 1152 | | /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), | 1153 | | * then this entire subroutine must be a hint. drop its call. */ | 1154 | 2.27k | if (drop.ends_in_hint) | 1155 | 60 | { | 1156 | 60 | str.values[pos].set_hinting (); | 1157 | | /* if this subr call is at the end of the parent subr, propagate the flag | 1158 | | * otherwise reset the flag */ | 1159 | 60 | if (!str.at_end (pos)) | 1160 | 43 | drop.ends_in_hint = false; | 1161 | 60 | } | 1162 | 2.21k | else if (drop.all_dropped) | 1163 | 331 | { | 1164 | 331 | str.values[pos].set_hinting (); | 1165 | 331 | } | 1166 | | | 1167 | 2.27k | return has_hint; | 1168 | 2.27k | } |
|
1169 | | |
1170 | | /* returns true if it sees a hint op before the first moveto */ |
1171 | | bool drop_hints_in_str (parsed_cs_str_t &str, const subr_subset_param_t ¶m, drop_hints_param_t &drop) |
1172 | 4.24k | { |
1173 | 4.24k | bool seen_hint = false; |
1174 | | |
1175 | 4.24k | unsigned count = str.values.length; |
1176 | 4.24k | if (unlikely (!hb_budget_spend (*drop.budget, count))) |
1177 | 0 | return false; |
1178 | 4.24k | auto *values = str.values.arrayZ; |
1179 | 108k | for (unsigned int pos = 0; pos < count; pos++) |
1180 | 104k | { |
1181 | 104k | bool has_hint = false; |
1182 | 104k | switch (values[pos].op) |
1183 | 104k | { |
1184 | 2.52k | case OpCode_callsubr: |
1185 | 2.52k | has_hint = drop_hints_in_subr (str, pos, |
1186 | 2.52k | *param.parsed_local_subrs, values[pos].subr_num, |
1187 | 2.52k | param, drop); |
1188 | 2.52k | break; |
1189 | | |
1190 | 309 | case OpCode_callgsubr: |
1191 | 309 | has_hint = drop_hints_in_subr (str, pos, |
1192 | 309 | *param.parsed_global_subrs, values[pos].subr_num, |
1193 | 309 | param, drop); |
1194 | 309 | break; |
1195 | | |
1196 | 1.46k | case OpCode_rmoveto: |
1197 | 3.09k | case OpCode_hmoveto: |
1198 | 4.03k | case OpCode_vmoveto: |
1199 | 4.03k | drop.seen_moveto = true; |
1200 | 4.03k | break; |
1201 | | |
1202 | 657 | case OpCode_hintmask: |
1203 | 1.68k | case OpCode_cntrmask: |
1204 | 1.68k | if (drop.seen_moveto) |
1205 | 1.05k | { |
1206 | 1.05k | values[pos].set_hinting (); |
1207 | 1.05k | break; |
1208 | 1.05k | } |
1209 | 637 | HB_FALLTHROUGH; |
1210 | | |
1211 | 3.55k | case OpCode_hstemhm: |
1212 | 4.28k | case OpCode_vstemhm: |
1213 | 6.92k | case OpCode_hstem: |
1214 | 8.34k | case OpCode_vstem: |
1215 | 8.34k | has_hint = true; |
1216 | 8.34k | values[pos].set_hinting (); |
1217 | 8.34k | if (str.at_end (pos)) |
1218 | 105 | drop.ends_in_hint = true; |
1219 | 8.34k | break; |
1220 | | |
1221 | 265 | case OpCode_dotsection: |
1222 | 265 | values[pos].set_hinting (); |
1223 | 265 | break; |
1224 | | |
1225 | 87.8k | default: |
1226 | | /* NONE */ |
1227 | 87.8k | break; |
1228 | 104k | } |
1229 | 104k | if (has_hint) |
1230 | 8.83k | { |
1231 | 43.6k | for (int i = pos - 1; i >= 0; i--) |
1232 | 42.5k | { |
1233 | 42.5k | parsed_cs_op_t &csop = values[(unsigned)i]; |
1234 | 42.5k | if (csop.is_hinting ()) |
1235 | 7.68k | break; |
1236 | 34.8k | csop.set_hinting (); |
1237 | 34.8k | if (csop.op == OpCode_vsindexcs) |
1238 | 109 | drop.vsindex_dropped = true; |
1239 | 34.8k | } |
1240 | 8.83k | seen_hint |= has_hint; |
1241 | 8.83k | } |
1242 | 104k | } |
1243 | | |
1244 | | /* Raise all_dropped flag if all operators except return are dropped from a subr. |
1245 | | * It may happen even after seeing the first moveto if a subr contains |
1246 | | * only (usually one) hintmask operator, then calls to this subr can be dropped. |
1247 | | */ |
1248 | 4.24k | drop.all_dropped = true; |
1249 | 37.2k | for (unsigned int pos = 0; pos < count; pos++) |
1250 | 36.7k | { |
1251 | 36.7k | parsed_cs_op_t &csop = values[pos]; |
1252 | 36.7k | if (csop.op == OpCode_return) |
1253 | 76 | break; |
1254 | 36.6k | if (!csop.is_hinting ()) |
1255 | 3.60k | { |
1256 | 3.60k | drop.all_dropped = false; |
1257 | 3.60k | break; |
1258 | 3.60k | } |
1259 | 36.6k | } |
1260 | | |
1261 | 4.24k | return seen_hint; |
1262 | 4.24k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_in_str(CFF::parsed_cs_str_t&, CFF::subr_subset_param_t const&, CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::drop_hints_param_t&) Line | Count | Source | 1172 | 1.54k | { | 1173 | 1.54k | bool seen_hint = false; | 1174 | | | 1175 | 1.54k | unsigned count = str.values.length; | 1176 | 1.54k | if (unlikely (!hb_budget_spend (*drop.budget, count))) | 1177 | 0 | return false; | 1178 | 1.54k | auto *values = str.values.arrayZ; | 1179 | 60.8k | for (unsigned int pos = 0; pos < count; pos++) | 1180 | 59.2k | { | 1181 | 59.2k | bool has_hint = false; | 1182 | 59.2k | switch (values[pos].op) | 1183 | 59.2k | { | 1184 | 355 | case OpCode_callsubr: | 1185 | 355 | has_hint = drop_hints_in_subr (str, pos, | 1186 | 355 | *param.parsed_local_subrs, values[pos].subr_num, | 1187 | 355 | param, drop); | 1188 | 355 | break; | 1189 | | | 1190 | 200 | case OpCode_callgsubr: | 1191 | 200 | has_hint = drop_hints_in_subr (str, pos, | 1192 | 200 | *param.parsed_global_subrs, values[pos].subr_num, | 1193 | 200 | param, drop); | 1194 | 200 | break; | 1195 | | | 1196 | 923 | case OpCode_rmoveto: | 1197 | 1.97k | case OpCode_hmoveto: | 1198 | 2.57k | case OpCode_vmoveto: | 1199 | 2.57k | drop.seen_moveto = true; | 1200 | 2.57k | break; | 1201 | | | 1202 | 436 | case OpCode_hintmask: | 1203 | 913 | case OpCode_cntrmask: | 1204 | 913 | if (drop.seen_moveto) | 1205 | 611 | { | 1206 | 611 | values[pos].set_hinting (); | 1207 | 611 | break; | 1208 | 611 | } | 1209 | 302 | HB_FALLTHROUGH; | 1210 | | | 1211 | 2.95k | case OpCode_hstemhm: | 1212 | 3.39k | case OpCode_vstemhm: | 1213 | 5.42k | case OpCode_hstem: | 1214 | 6.41k | case OpCode_vstem: | 1215 | 6.41k | has_hint = true; | 1216 | 6.41k | values[pos].set_hinting (); | 1217 | 6.41k | if (str.at_end (pos)) | 1218 | 33 | drop.ends_in_hint = true; | 1219 | 6.41k | break; | 1220 | | | 1221 | 234 | case OpCode_dotsection: | 1222 | 234 | values[pos].set_hinting (); | 1223 | 234 | break; | 1224 | | | 1225 | 48.8k | default: | 1226 | | /* NONE */ | 1227 | 48.8k | break; | 1228 | 59.2k | } | 1229 | 59.2k | if (has_hint) | 1230 | 6.71k | { | 1231 | 32.0k | for (int i = pos - 1; i >= 0; i--) | 1232 | 31.1k | { | 1233 | 31.1k | parsed_cs_op_t &csop = values[(unsigned)i]; | 1234 | 31.1k | if (csop.is_hinting ()) | 1235 | 5.80k | break; | 1236 | 25.3k | csop.set_hinting (); | 1237 | 25.3k | if (csop.op == OpCode_vsindexcs) | 1238 | 105 | drop.vsindex_dropped = true; | 1239 | 25.3k | } | 1240 | 6.71k | seen_hint |= has_hint; | 1241 | 6.71k | } | 1242 | 59.2k | } | 1243 | | | 1244 | | /* Raise all_dropped flag if all operators except return are dropped from a subr. | 1245 | | * It may happen even after seeing the first moveto if a subr contains | 1246 | | * only (usually one) hintmask operator, then calls to this subr can be dropped. | 1247 | | */ | 1248 | 1.54k | drop.all_dropped = true; | 1249 | 24.9k | for (unsigned int pos = 0; pos < count; pos++) | 1250 | 24.9k | { | 1251 | 24.9k | parsed_cs_op_t &csop = values[pos]; | 1252 | 24.9k | if (csop.op == OpCode_return) | 1253 | 76 | break; | 1254 | 24.8k | if (!csop.is_hinting ()) | 1255 | 1.47k | { | 1256 | 1.47k | drop.all_dropped = false; | 1257 | 1.47k | break; | 1258 | 1.47k | } | 1259 | 24.8k | } | 1260 | | | 1261 | 1.54k | return seen_hint; | 1262 | 1.54k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_in_str(CFF::parsed_cs_str_t&, CFF::subr_subset_param_t const&, CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::drop_hints_param_t&) Line | Count | Source | 1172 | 2.69k | { | 1173 | 2.69k | bool seen_hint = false; | 1174 | | | 1175 | 2.69k | unsigned count = str.values.length; | 1176 | 2.69k | if (unlikely (!hb_budget_spend (*drop.budget, count))) | 1177 | 0 | return false; | 1178 | 2.69k | auto *values = str.values.arrayZ; | 1179 | 47.7k | for (unsigned int pos = 0; pos < count; pos++) | 1180 | 45.0k | { | 1181 | 45.0k | bool has_hint = false; | 1182 | 45.0k | switch (values[pos].op) | 1183 | 45.0k | { | 1184 | 2.16k | case OpCode_callsubr: | 1185 | 2.16k | has_hint = drop_hints_in_subr (str, pos, | 1186 | 2.16k | *param.parsed_local_subrs, values[pos].subr_num, | 1187 | 2.16k | param, drop); | 1188 | 2.16k | break; | 1189 | | | 1190 | 109 | case OpCode_callgsubr: | 1191 | 109 | has_hint = drop_hints_in_subr (str, pos, | 1192 | 109 | *param.parsed_global_subrs, values[pos].subr_num, | 1193 | 109 | param, drop); | 1194 | 109 | break; | 1195 | | | 1196 | 542 | case OpCode_rmoveto: | 1197 | 1.12k | case OpCode_hmoveto: | 1198 | 1.45k | case OpCode_vmoveto: | 1199 | 1.45k | drop.seen_moveto = true; | 1200 | 1.45k | break; | 1201 | | | 1202 | 221 | case OpCode_hintmask: | 1203 | 774 | case OpCode_cntrmask: | 1204 | 774 | if (drop.seen_moveto) | 1205 | 439 | { | 1206 | 439 | values[pos].set_hinting (); | 1207 | 439 | break; | 1208 | 439 | } | 1209 | 335 | HB_FALLTHROUGH; | 1210 | | | 1211 | 599 | case OpCode_hstemhm: | 1212 | 890 | case OpCode_vstemhm: | 1213 | 1.49k | case OpCode_hstem: | 1214 | 1.93k | case OpCode_vstem: | 1215 | 1.93k | has_hint = true; | 1216 | 1.93k | values[pos].set_hinting (); | 1217 | 1.93k | if (str.at_end (pos)) | 1218 | 72 | drop.ends_in_hint = true; | 1219 | 1.93k | break; | 1220 | | | 1221 | 31 | case OpCode_dotsection: | 1222 | 31 | values[pos].set_hinting (); | 1223 | 31 | break; | 1224 | | | 1225 | 38.9k | default: | 1226 | | /* NONE */ | 1227 | 38.9k | break; | 1228 | 45.0k | } | 1229 | 45.0k | if (has_hint) | 1230 | 2.12k | { | 1231 | 11.5k | for (int i = pos - 1; i >= 0; i--) | 1232 | 11.3k | { | 1233 | 11.3k | parsed_cs_op_t &csop = values[(unsigned)i]; | 1234 | 11.3k | if (csop.is_hinting ()) | 1235 | 1.87k | break; | 1236 | 9.47k | csop.set_hinting (); | 1237 | 9.47k | if (csop.op == OpCode_vsindexcs) | 1238 | 4 | drop.vsindex_dropped = true; | 1239 | 9.47k | } | 1240 | 2.12k | seen_hint |= has_hint; | 1241 | 2.12k | } | 1242 | 45.0k | } | 1243 | | | 1244 | | /* Raise all_dropped flag if all operators except return are dropped from a subr. | 1245 | | * It may happen even after seeing the first moveto if a subr contains | 1246 | | * only (usually one) hintmask operator, then calls to this subr can be dropped. | 1247 | | */ | 1248 | 2.69k | drop.all_dropped = true; | 1249 | 12.3k | for (unsigned int pos = 0; pos < count; pos++) | 1250 | 11.7k | { | 1251 | 11.7k | parsed_cs_op_t &csop = values[pos]; | 1252 | 11.7k | if (csop.op == OpCode_return) | 1253 | 0 | break; | 1254 | 11.7k | if (!csop.is_hinting ()) | 1255 | 2.13k | { | 1256 | 2.13k | drop.all_dropped = false; | 1257 | 2.13k | break; | 1258 | 2.13k | } | 1259 | 11.7k | } | 1260 | | | 1261 | 2.69k | return seen_hint; | 1262 | 2.69k | } |
|
1263 | | |
1264 | | bool closure_subroutines (const parsed_cs_str_vec_t& global_subrs, |
1265 | | const hb_vector_t<parsed_cs_str_vec_t>& local_subrs) |
1266 | 948 | { |
1267 | 948 | closures.reset (); |
1268 | 948 | for (auto _ : plan->new_to_old_gid_list) |
1269 | 1.36k | { |
1270 | 1.36k | hb_codepoint_t new_glyph = _.first; |
1271 | 1.36k | hb_codepoint_t old_glyph = _.second; |
1272 | 1.36k | unsigned int fd = subset_get_fd (acc, old_glyph); |
1273 | 1.36k | if (unlikely (fd >= acc.fdCount)) |
1274 | 0 | return false; |
1275 | | |
1276 | | // Note: const cast is safe here because the collect_subr_refs_in_str only performs a |
1277 | | // closure and does not modify any of the charstrings. |
1278 | 1.36k | subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)), |
1279 | 1.36k | const_cast<parsed_cs_str_vec_t*> (&global_subrs), |
1280 | 1.36k | const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]), |
1281 | 1.36k | &closures.global_closure, |
1282 | 1.36k | &closures.local_closures[fd], |
1283 | 1.36k | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); |
1284 | 1.36k | collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param); |
1285 | 1.36k | } |
1286 | | |
1287 | 948 | return true; |
1288 | 948 | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::closure_subroutines(CFF::parsed_cs_str_vec_t const&, hb_vector_t<CFF::parsed_cs_str_vec_t, false> const&) Line | Count | Source | 1266 | 711 | { | 1267 | 711 | closures.reset (); | 1268 | 711 | for (auto _ : plan->new_to_old_gid_list) | 1269 | 980 | { | 1270 | 980 | hb_codepoint_t new_glyph = _.first; | 1271 | 980 | hb_codepoint_t old_glyph = _.second; | 1272 | 980 | unsigned int fd = subset_get_fd (acc, old_glyph); | 1273 | 980 | if (unlikely (fd >= acc.fdCount)) | 1274 | 0 | return false; | 1275 | | | 1276 | | // Note: const cast is safe here because the collect_subr_refs_in_str only performs a | 1277 | | // closure and does not modify any of the charstrings. | 1278 | 980 | subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)), | 1279 | 980 | const_cast<parsed_cs_str_vec_t*> (&global_subrs), | 1280 | 980 | const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]), | 1281 | 980 | &closures.global_closure, | 1282 | 980 | &closures.local_closures[fd], | 1283 | 980 | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 1284 | 980 | collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param); | 1285 | 980 | } | 1286 | | | 1287 | 711 | return true; | 1288 | 711 | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::closure_subroutines(CFF::parsed_cs_str_vec_t const&, hb_vector_t<CFF::parsed_cs_str_vec_t, false> const&) Line | Count | Source | 1266 | 237 | { | 1267 | 237 | closures.reset (); | 1268 | 237 | for (auto _ : plan->new_to_old_gid_list) | 1269 | 381 | { | 1270 | 381 | hb_codepoint_t new_glyph = _.first; | 1271 | 381 | hb_codepoint_t old_glyph = _.second; | 1272 | 381 | unsigned int fd = subset_get_fd (acc, old_glyph); | 1273 | 381 | if (unlikely (fd >= acc.fdCount)) | 1274 | 0 | return false; | 1275 | | | 1276 | | // Note: const cast is safe here because the collect_subr_refs_in_str only performs a | 1277 | | // closure and does not modify any of the charstrings. | 1278 | 381 | subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)), | 1279 | 381 | const_cast<parsed_cs_str_vec_t*> (&global_subrs), | 1280 | 381 | const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]), | 1281 | 381 | &closures.global_closure, | 1282 | 381 | &closures.local_closures[fd], | 1283 | 381 | plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 1284 | 381 | collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param); | 1285 | 381 | } | 1286 | | | 1287 | 237 | return true; | 1288 | 237 | } |
|
1289 | | |
1290 | | void collect_subr_refs_in_subr (unsigned int subr_num, parsed_cs_str_vec_t &subrs, |
1291 | | hb_set_t *closure, |
1292 | | const subr_subset_param_t ¶m, |
1293 | | unsigned int depth) |
1294 | 1.51k | { |
1295 | 1.51k | if (closure->has (subr_num)) |
1296 | 537 | return; |
1297 | 973 | closure->add (subr_num); |
1298 | | /* Too deep to walk into; keep the subr but not what it calls. */ |
1299 | 973 | if (unlikely (depth >= kMaxCallLimit)) |
1300 | 0 | return; |
1301 | 973 | collect_subr_refs_in_str (subrs[subr_num], param, depth + 1); |
1302 | 973 | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::collect_subr_refs_in_subr(unsigned int, CFF::parsed_cs_str_vec_t&, hb_set_t*, CFF::subr_subset_param_t const&, unsigned int) Line | Count | Source | 1294 | 343 | { | 1295 | 343 | if (closure->has (subr_num)) | 1296 | 88 | return; | 1297 | 255 | closure->add (subr_num); | 1298 | | /* Too deep to walk into; keep the subr but not what it calls. */ | 1299 | 255 | if (unlikely (depth >= kMaxCallLimit)) | 1300 | 0 | return; | 1301 | 255 | collect_subr_refs_in_str (subrs[subr_num], param, depth + 1); | 1302 | 255 | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::collect_subr_refs_in_subr(unsigned int, CFF::parsed_cs_str_vec_t&, hb_set_t*, CFF::subr_subset_param_t const&, unsigned int) Line | Count | Source | 1294 | 1.16k | { | 1295 | 1.16k | if (closure->has (subr_num)) | 1296 | 449 | return; | 1297 | 718 | closure->add (subr_num); | 1298 | | /* Too deep to walk into; keep the subr but not what it calls. */ | 1299 | 718 | if (unlikely (depth >= kMaxCallLimit)) | 1300 | 0 | return; | 1301 | 718 | collect_subr_refs_in_str (subrs[subr_num], param, depth + 1); | 1302 | 718 | } |
|
1303 | | |
1304 | | void collect_subr_refs_in_str (const parsed_cs_str_t &str, |
1305 | | const subr_subset_param_t ¶m, |
1306 | | unsigned int depth = 0) |
1307 | 2.33k | { |
1308 | 2.33k | if (!str.has_calls ()) |
1309 | 1.57k | return; |
1310 | | |
1311 | 761 | for (auto &opstr : str.values) |
1312 | 14.8k | { |
1313 | 14.8k | if (!param.drop_hints || !opstr.is_hinting ()) |
1314 | 11.2k | { |
1315 | 11.2k | switch (opstr.op) |
1316 | 11.2k | { |
1317 | 1.33k | case OpCode_callsubr: |
1318 | 1.33k | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs, |
1319 | 1.33k | param.local_closure, param, depth); |
1320 | 1.33k | break; |
1321 | | |
1322 | 174 | case OpCode_callgsubr: |
1323 | 174 | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs, |
1324 | 174 | param.global_closure, param, depth); |
1325 | 174 | break; |
1326 | | |
1327 | 9.75k | default: break; |
1328 | 11.2k | } |
1329 | 11.2k | } |
1330 | 14.8k | } |
1331 | 761 | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::collect_subr_refs_in_str(CFF::parsed_cs_str_t const&, CFF::subr_subset_param_t const&, unsigned int) Line | Count | Source | 1307 | 1.23k | { | 1308 | 1.23k | if (!str.has_calls ()) | 1309 | 912 | return; | 1310 | | | 1311 | 323 | for (auto &opstr : str.values) | 1312 | 3.61k | { | 1313 | 3.61k | if (!param.drop_hints || !opstr.is_hinting ()) | 1314 | 1.48k | { | 1315 | 1.48k | switch (opstr.op) | 1316 | 1.48k | { | 1317 | 197 | case OpCode_callsubr: | 1318 | 197 | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs, | 1319 | 197 | param.local_closure, param, depth); | 1320 | 197 | break; | 1321 | | | 1322 | 146 | case OpCode_callgsubr: | 1323 | 146 | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs, | 1324 | 146 | param.global_closure, param, depth); | 1325 | 146 | break; | 1326 | | | 1327 | 1.14k | default: break; | 1328 | 1.48k | } | 1329 | 1.48k | } | 1330 | 3.61k | } | 1331 | 323 | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::collect_subr_refs_in_str(CFF::parsed_cs_str_t const&, CFF::subr_subset_param_t const&, unsigned int) Line | Count | Source | 1307 | 1.09k | { | 1308 | 1.09k | if (!str.has_calls ()) | 1309 | 661 | return; | 1310 | | | 1311 | 438 | for (auto &opstr : str.values) | 1312 | 11.2k | { | 1313 | 11.2k | if (!param.drop_hints || !opstr.is_hinting ()) | 1314 | 9.78k | { | 1315 | 9.78k | switch (opstr.op) | 1316 | 9.78k | { | 1317 | 1.13k | case OpCode_callsubr: | 1318 | 1.13k | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs, | 1319 | 1.13k | param.local_closure, param, depth); | 1320 | 1.13k | break; | 1321 | | | 1322 | 28 | case OpCode_callgsubr: | 1323 | 28 | collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs, | 1324 | 28 | param.global_closure, param, depth); | 1325 | 28 | break; | 1326 | | | 1327 | 8.61k | default: break; | 1328 | 9.78k | } | 1329 | 9.78k | } | 1330 | 11.2k | } | 1331 | 438 | } |
|
1332 | | |
1333 | | bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff, bool encode_prefix = true) const |
1334 | 25.1k | { |
1335 | 25.1k | str_encoder_t encoder (buff); |
1336 | 25.1k | encoder.reset (); |
1337 | 25.1k | bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); |
1338 | | /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, |
1339 | | * re-insert it at the beginning of charstreing */ |
1340 | 25.1k | if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) |
1341 | 289 | { |
1342 | 289 | encoder.encode_num_cs (str.prefix_num ()); |
1343 | 289 | if (str.prefix_op () != OpCode_Invalid) |
1344 | 5 | encoder.encode_op (str.prefix_op ()); |
1345 | 289 | } |
1346 | | |
1347 | 25.1k | unsigned size = 0; |
1348 | 25.1k | for (auto &opstr : str.values) |
1349 | 85.4k | { |
1350 | 85.4k | size += opstr.length; |
1351 | 85.4k | if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) |
1352 | 22.0k | size += 3; |
1353 | 85.4k | } |
1354 | 25.1k | if (!buff.alloc_exact (buff.length + size)) |
1355 | 3 | return false; |
1356 | | |
1357 | 25.1k | for (auto &opstr : str.values) |
1358 | 85.4k | { |
1359 | 85.4k | if (hinting || !opstr.is_hinting ()) |
1360 | 71.7k | { |
1361 | 71.7k | switch (opstr.op) |
1362 | 71.7k | { |
1363 | 19.8k | case OpCode_callsubr: |
1364 | 19.8k | encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); |
1365 | 19.8k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); |
1366 | 19.8k | break; |
1367 | | |
1368 | 1.52k | case OpCode_callgsubr: |
1369 | 1.52k | encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); |
1370 | 1.52k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); |
1371 | 1.52k | break; |
1372 | | |
1373 | 50.4k | default: |
1374 | 50.4k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); |
1375 | 50.4k | break; |
1376 | 71.7k | } |
1377 | 71.7k | } |
1378 | 85.4k | } |
1379 | 25.1k | return !encoder.in_error (); |
1380 | 25.1k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::encode_str(CFF::parsed_cs_str_t const&, unsigned int, hb_vector_t<unsigned char, false>&, bool) const Line | Count | Source | 1334 | 10.1k | { | 1335 | 10.1k | str_encoder_t encoder (buff); | 1336 | 10.1k | encoder.reset (); | 1337 | 10.1k | bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 1338 | | /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, | 1339 | | * re-insert it at the beginning of charstreing */ | 1340 | 10.1k | if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) | 1341 | 284 | { | 1342 | 284 | encoder.encode_num_cs (str.prefix_num ()); | 1343 | 284 | if (str.prefix_op () != OpCode_Invalid) | 1344 | 0 | encoder.encode_op (str.prefix_op ()); | 1345 | 284 | } | 1346 | | | 1347 | 10.1k | unsigned size = 0; | 1348 | 10.1k | for (auto &opstr : str.values) | 1349 | 31.1k | { | 1350 | 31.1k | size += opstr.length; | 1351 | 31.1k | if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) | 1352 | 3.43k | size += 3; | 1353 | 31.1k | } | 1354 | 10.1k | if (!buff.alloc_exact (buff.length + size)) | 1355 | 1 | return false; | 1356 | | | 1357 | 10.1k | for (auto &opstr : str.values) | 1358 | 31.1k | { | 1359 | 31.1k | if (hinting || !opstr.is_hinting ()) | 1360 | 19.4k | { | 1361 | 19.4k | switch (opstr.op) | 1362 | 19.4k | { | 1363 | 2.05k | case OpCode_callsubr: | 1364 | 2.05k | encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); | 1365 | 2.05k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1366 | 2.05k | break; | 1367 | | | 1368 | 1.22k | case OpCode_callgsubr: | 1369 | 1.22k | encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); | 1370 | 1.22k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1371 | 1.22k | break; | 1372 | | | 1373 | 16.1k | default: | 1374 | 16.1k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1375 | 16.1k | break; | 1376 | 19.4k | } | 1377 | 19.4k | } | 1378 | 31.1k | } | 1379 | 10.1k | return !encoder.in_error (); | 1380 | 10.1k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::encode_str(CFF::parsed_cs_str_t const&, unsigned int, hb_vector_t<unsigned char, false>&, bool) const Line | Count | Source | 1334 | 14.9k | { | 1335 | 14.9k | str_encoder_t encoder (buff); | 1336 | 14.9k | encoder.reset (); | 1337 | 14.9k | bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); | 1338 | | /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, | 1339 | | * re-insert it at the beginning of charstreing */ | 1340 | 14.9k | if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) | 1341 | 5 | { | 1342 | 5 | encoder.encode_num_cs (str.prefix_num ()); | 1343 | 5 | if (str.prefix_op () != OpCode_Invalid) | 1344 | 5 | encoder.encode_op (str.prefix_op ()); | 1345 | 5 | } | 1346 | | | 1347 | 14.9k | unsigned size = 0; | 1348 | 14.9k | for (auto &opstr : str.values) | 1349 | 54.3k | { | 1350 | 54.3k | size += opstr.length; | 1351 | 54.3k | if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) | 1352 | 18.5k | size += 3; | 1353 | 54.3k | } | 1354 | 14.9k | if (!buff.alloc_exact (buff.length + size)) | 1355 | 2 | return false; | 1356 | | | 1357 | 14.9k | for (auto &opstr : str.values) | 1358 | 54.3k | { | 1359 | 54.3k | if (hinting || !opstr.is_hinting ()) | 1360 | 52.2k | { | 1361 | 52.2k | switch (opstr.op) | 1362 | 52.2k | { | 1363 | 17.7k | case OpCode_callsubr: | 1364 | 17.7k | encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); | 1365 | 17.7k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1366 | 17.7k | break; | 1367 | | | 1368 | 296 | case OpCode_callgsubr: | 1369 | 296 | encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); | 1370 | 296 | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1371 | 296 | break; | 1372 | | | 1373 | 34.2k | default: | 1374 | 34.2k | encoder.copy_str (hb_ubytes_t (opstr.ptr, opstr.length)); | 1375 | 34.2k | break; | 1376 | 52.2k | } | 1377 | 52.2k | } | 1378 | 54.3k | } | 1379 | 14.9k | return !encoder.in_error (); | 1380 | 14.9k | } |
|
1381 | | |
1382 | | void compact_parsed_subrs () const |
1383 | 0 | { |
1384 | 0 | for (auto &cs : parsed_global_subrs_storage) |
1385 | 0 | cs.compact (); |
1386 | 0 | for (auto &vec : parsed_local_subrs_storage) |
1387 | 0 | for (auto &cs : vec) |
1388 | 0 | cs.compact (); |
1389 | 0 | } Unexecuted instantiation: CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::compact_parsed_subrs() const Unexecuted instantiation: CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::compact_parsed_subrs() const |
1390 | | |
1391 | | void populate_subset_accelerator () const |
1392 | 5.07k | { |
1393 | 5.07k | if (!plan->inprogress_accelerator) return; |
1394 | | |
1395 | 0 | compact_parsed_subrs (); |
1396 | |
|
1397 | 0 | acc.cff_accelerator = |
1398 | 0 | cff_subset_accelerator_t::create(acc.blob, |
1399 | 0 | parsed_charstrings, |
1400 | 0 | parsed_global_subrs_storage, |
1401 | 0 | parsed_local_subrs_storage); |
1402 | 0 | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::populate_subset_accelerator() const Line | Count | Source | 1392 | 3.81k | { | 1393 | 3.81k | if (!plan->inprogress_accelerator) return; | 1394 | | | 1395 | 0 | compact_parsed_subrs (); | 1396 | |
| 1397 | 0 | acc.cff_accelerator = | 1398 | 0 | cff_subset_accelerator_t::create(acc.blob, | 1399 | 0 | parsed_charstrings, | 1400 | 0 | parsed_global_subrs_storage, | 1401 | 0 | parsed_local_subrs_storage); | 1402 | 0 | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::populate_subset_accelerator() const Line | Count | Source | 1392 | 1.25k | { | 1393 | 1.25k | if (!plan->inprogress_accelerator) return; | 1394 | | | 1395 | 0 | compact_parsed_subrs (); | 1396 | |
| 1397 | 0 | acc.cff_accelerator = | 1398 | 0 | cff_subset_accelerator_t::create(acc.blob, | 1399 | 0 | parsed_charstrings, | 1400 | 0 | parsed_global_subrs_storage, | 1401 | 0 | parsed_local_subrs_storage); | 1402 | 0 | } |
|
1403 | | |
1404 | | const parsed_cs_str_t& get_parsed_charstring (unsigned i) const |
1405 | 14.5k | { |
1406 | 14.5k | if (cached_charstrings) return *(cached_charstrings[i]); |
1407 | 14.5k | return parsed_charstrings[i]; |
1408 | 14.5k | } CFF::subr_subsetter_t<cff1_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned short, 2u> >, OT::cff1::accelerator_subset_t const, CFF::cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, 14u>::get_parsed_charstring(unsigned int) const Line | Count | Source | 1405 | 10.1k | { | 1406 | 10.1k | if (cached_charstrings) return *(cached_charstrings[i]); | 1407 | 10.1k | return parsed_charstrings[i]; | 1408 | 10.1k | } |
CFF::subr_subsetter_t<cff2_subr_subsetter_t, CFF::Subrs<OT::NumType<true, unsigned int, 4u> >, OT::cff2::accelerator_subset_t const, CFF::cff2_cs_interp_env_t<CFF::blend_arg_t>, cff2_cs_opset_subr_subset_t, 65535u>::get_parsed_charstring(unsigned int) const Line | Count | Source | 1405 | 4.37k | { | 1406 | 4.37k | if (cached_charstrings) return *(cached_charstrings[i]); | 1407 | 4.37k | return parsed_charstrings[i]; | 1408 | 4.37k | } |
|
1409 | | |
1410 | | protected: |
1411 | | const ACC &acc; |
1412 | | const hb_subset_plan_t *plan; |
1413 | | |
1414 | | subr_closures_t closures; |
1415 | | |
1416 | | hb_vector_t<const parsed_cs_str_t*> cached_charstrings; |
1417 | | const parsed_cs_str_vec_t* parsed_global_subrs; |
1418 | | const hb_vector_t<parsed_cs_str_vec_t>* parsed_local_subrs; |
1419 | | |
1420 | | subr_remaps_t remaps; |
1421 | | |
1422 | | private: |
1423 | | |
1424 | | parsed_cs_str_vec_t parsed_charstrings; |
1425 | | parsed_cs_str_vec_t parsed_global_subrs_storage; |
1426 | | hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs_storage; |
1427 | | typedef typename SUBRS::count_type subr_count_type; |
1428 | | }; |
1429 | | |
1430 | | } /* namespace CFF */ |
1431 | | |
1432 | | template <typename FDSELECT> |
1433 | | HB_INTERNAL bool |
1434 | | hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, |
1435 | | unsigned int fdCount, |
1436 | | const FDSELECT &src, /* IN */ |
1437 | | unsigned int src_num_glyphs, |
1438 | | unsigned int &subset_fd_count /* OUT */, |
1439 | | unsigned int &subset_fdselect_size /* OUT */, |
1440 | | unsigned int &subset_fdselect_format /* OUT */, |
1441 | | hb_vector_t<CFF::code_pair_t> &fdselect_ranges /* OUT */, |
1442 | | hb_inc_bimap_t &fdmap /* OUT */); |
1443 | | |
1444 | | HB_INTERNAL bool |
1445 | | hb_serialize_cff_fdselect (hb_serialize_context_t *c, |
1446 | | unsigned int num_glyphs, |
1447 | | const CFF::FDSelect &src, |
1448 | | unsigned int fd_count, |
1449 | | unsigned int fdselect_format, |
1450 | | unsigned int size, |
1451 | | const hb_vector_t<CFF::code_pair_t> &fdselect_ranges); |
1452 | | |
1453 | | #endif /* HB_SUBSET_CFF_COMMON_HH */ |