Coverage Report

Created: 2026-11-06 11:29

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