Coverage Report

Created: 2026-07-16 06:17

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