Coverage Report

Created: 2026-11-12 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 &param, 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 &param, 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 &param,
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 &param,
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 */