Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-subset-cff1.cc
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
#include "hb.hh"
28
29
#ifndef HB_NO_SUBSET_CFF
30
31
#include "hb-open-type.hh"
32
#include "hb-ot-cff1-table.hh"
33
#include "hb-set.h"
34
#include "hb-bimap.hh"
35
#include "hb-subset-plan.hh"
36
#include "hb-subset-cff-common.hh"
37
#include "hb-cff1-interp-cs.hh"
38
39
using namespace CFF;
40
41
struct remap_sid_t
42
{
43
0
  unsigned get_population () const { return vector.length; }
44
45
  void alloc (unsigned size)
46
0
  {
47
0
    map.alloc (size);
48
0
    vector.alloc_exact (size);
49
0
  }
50
51
  bool in_error () const
52
0
  { return map.in_error () || vector.in_error (); }
53
54
  unsigned int add (unsigned int sid)
55
0
  {
56
0
    if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
57
0
      return sid;
58
59
0
    sid = unoffset_sid (sid);
60
0
    unsigned v = next;
61
0
    if (map.set (sid, v, false))
62
0
    {
63
0
      vector.push (sid);
64
0
      next++;
65
0
    }
66
0
    else
67
0
      v = map.get (sid); // already exists
68
0
    return offset_sid (v);
69
0
  }
70
71
  unsigned int operator[] (unsigned int sid) const
72
0
  {
73
0
    if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
74
0
      return sid;
75
76
0
    return offset_sid (map.get (unoffset_sid (sid)));
77
0
  }
78
79
  static const unsigned int num_std_strings = 391;
80
81
0
  static bool is_std_str (unsigned int sid) { return sid < num_std_strings; }
82
0
  static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
83
0
  static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
84
  unsigned next = 0;
85
86
  hb_map_t map;
87
  hb_vector_t<unsigned> vector;
88
};
89
90
struct cff1_sub_table_info_t : cff_sub_table_info_t
91
{
92
  cff1_sub_table_info_t ()
93
0
    : cff_sub_table_info_t (),
94
0
      encoding_link (0),
95
0
      charset_link (0)
96
0
   {
97
0
    privateDictInfo.init ();
98
0
  }
99
100
  objidx_t  encoding_link;
101
  objidx_t  charset_link;
102
  table_info_t  privateDictInfo;
103
};
104
105
/* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */
106
struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t
107
{
108
  void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t))
109
0
  {
110
0
    SUPER::init ();
111
0
    base = base_;
112
0
  }
113
114
0
  void fini () { SUPER::fini (); }
115
116
0
  unsigned get_count () const { return base->get_count () + SUPER::get_count (); }
117
  const cff1_top_dict_val_t &get_value (unsigned int i) const
118
0
  {
119
0
    if (i < base->get_count ())
120
0
      return (*base)[i];
121
0
    else
122
0
      return SUPER::values[i - base->get_count ()];
123
0
  }
124
0
  const cff1_top_dict_val_t &operator [] (unsigned int i) const { return get_value (i); }
125
126
  void reassignSIDs (const remap_sid_t& sidmap)
127
0
  {
128
0
    for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
129
0
      nameSIDs[i] = sidmap[base->nameSIDs[i]];
130
0
  }
131
132
  protected:
133
  typedef cff1_top_dict_values_t SUPER;
134
  const cff1_top_dict_values_t *base;
135
};
136
137
struct top_dict_modifiers_t
138
{
139
  top_dict_modifiers_t (const cff1_sub_table_info_t &info_,
140
      const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount])
141
0
    : info (info_),
142
0
      nameSIDs (nameSIDs_)
143
0
  {}
144
145
  const cff1_sub_table_info_t &info;
146
  const unsigned int  (&nameSIDs)[name_dict_values_t::ValCount];
147
};
148
149
struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dict_val_t>
150
{
151
  bool serialize (hb_serialize_context_t *c,
152
      const cff1_top_dict_val_t &opstr,
153
      const top_dict_modifiers_t &mod) const
154
0
  {
155
0
    TRACE_SERIALIZE (this);
156
157
0
    op_code_t op = opstr.op;
158
0
    switch (op)
159
0
    {
160
0
      case OpCode_charset:
161
0
  if (mod.info.charset_link)
162
0
    return_trace (FontDict::serialize_link4_op(c, op, mod.info.charset_link, whence_t::Absolute));
163
0
  else
164
0
    goto fall_back;
165
166
0
      case OpCode_Encoding:
167
0
  if (mod.info.encoding_link)
168
0
    return_trace (FontDict::serialize_link4_op(c, op, mod.info.encoding_link, whence_t::Absolute));
169
0
  else
170
0
    goto fall_back;
171
172
0
      case OpCode_Private:
173
0
  return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) &&
174
0
          Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute));
175
176
0
      case OpCode_version:
177
0
      case OpCode_Notice:
178
0
      case OpCode_Copyright:
179
0
      case OpCode_FullName:
180
0
      case OpCode_FamilyName:
181
0
      case OpCode_Weight:
182
0
      case OpCode_PostScript:
183
0
      case OpCode_BaseFontName:
184
0
      case OpCode_FontName:
185
0
  return_trace (FontDict::serialize_int2_op (c, op, mod.nameSIDs[name_dict_values_t::name_op_to_index (op)]));
186
187
0
      case OpCode_ROS:
188
0
  {
189
    /* for registry & ordering, reassigned SIDs are serialized
190
     * for supplement, the original byte string is copied along with the op code */
191
0
    op_str_t supp_op;
192
0
    supp_op.op = op;
193
0
    if ( unlikely (!(opstr.length >= opstr.last_arg_offset + 3)))
194
0
      return_trace (false);
195
0
    supp_op.ptr = opstr.ptr + opstr.last_arg_offset;
196
0
    supp_op.length = opstr.length - opstr.last_arg_offset;
197
0
    return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) &&
198
0
      UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) &&
199
0
      copy_opstr (c, supp_op));
200
0
  }
201
0
      fall_back:
202
0
      default:
203
0
  return_trace (cff_top_dict_op_serializer_t<cff1_top_dict_val_t>::serialize (c, opstr, mod.info));
204
0
    }
205
0
    return_trace (true);
206
0
  }
207
208
};
209
210
struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t
211
{
212
  bool serialize (hb_serialize_context_t *c,
213
      const op_str_t &opstr,
214
      const cff1_font_dict_values_mod_t &mod) const
215
0
  {
216
0
    TRACE_SERIALIZE (this);
217
218
0
    if (opstr.op == OpCode_FontName)
219
0
      return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName));
220
0
    else
221
0
      return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo));
222
0
  }
223
224
  private:
225
  typedef cff_font_dict_op_serializer_t SUPER;
226
};
227
228
struct cff1_cs_opset_flatten_t : cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t>
229
{
230
  static void flush_args_and_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
231
0
  {
232
0
    if (env.arg_start > 0)
233
0
      flush_width (env, param);
234
235
0
    switch (op)
236
0
    {
237
0
      case OpCode_hstem:
238
0
      case OpCode_hstemhm:
239
0
      case OpCode_vstem:
240
0
      case OpCode_vstemhm:
241
0
      case OpCode_hintmask:
242
0
      case OpCode_cntrmask:
243
0
      case OpCode_dotsection:
244
0
  if (param.drop_hints)
245
0
  {
246
0
    env.clear_args ();
247
0
    return;
248
0
  }
249
0
  HB_FALLTHROUGH;
250
251
0
      default:
252
0
  SUPER::flush_args_and_op (op, env, param);
253
0
  break;
254
0
    }
255
0
  }
256
  static void flush_args (cff1_cs_interp_env_t &env, flatten_param_t& param)
257
0
  {
258
0
    str_encoder_t  encoder (param.flatStr);
259
0
    for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
260
0
      encoder.encode_num_cs (env.eval_arg (i));
261
0
    SUPER::flush_args (env, param);
262
0
  }
263
264
  static void flush_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
265
0
  {
266
0
    str_encoder_t  encoder (param.flatStr);
267
0
    encoder.encode_op (op);
268
0
  }
269
270
  static void flush_width (cff1_cs_interp_env_t &env, flatten_param_t& param)
271
0
  {
272
0
    assert (env.has_width);
273
0
    str_encoder_t  encoder (param.flatStr);
274
0
    encoder.encode_num_cs (env.width);
275
0
  }
276
277
  static void flush_hintmask (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param)
278
0
  {
279
0
    SUPER::flush_hintmask (op, env, param);
280
0
    if (!param.drop_hints)
281
0
    {
282
0
      str_encoder_t  encoder (param.flatStr);
283
0
      for (unsigned int i = 0; i < env.hintmask_size; i++)
284
0
  encoder.encode_byte (env.str_ref[i]);
285
0
    }
286
0
  }
287
288
  private:
289
  typedef cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> SUPER;
290
};
291
292
struct range_list_t : hb_vector_t<code_pair_t>
293
{
294
  /* replace the first glyph ID in the "glyph" field each range with a nLeft value */
295
  bool complete (unsigned int last_glyph)
296
0
  {
297
0
    hb_codepoint_t all_glyphs = 0;
298
0
    unsigned count = this->length;
299
0
    for (unsigned int i = count; i; i--)
300
0
    {
301
0
      code_pair_t &pair = arrayZ[i - 1];
302
0
      unsigned int nLeft = last_glyph - pair.glyph - 1;
303
0
      all_glyphs |= nLeft;
304
0
      last_glyph = pair.glyph;
305
0
      pair.glyph = nLeft;
306
0
    }
307
0
    bool two_byte = all_glyphs >= 0x100;
308
0
    return two_byte;
309
0
  }
310
};
311
312
struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t>
313
{
314
  static void process_op (op_code_t op, cff1_cs_interp_env_t &env, subr_subset_param_t& param)
315
0
  {
316
0
    switch (op) {
317
318
0
      case OpCode_return:
319
0
  param.current_parsed_str->add_op (op, env.str_ref);
320
0
  param.current_parsed_str->set_parsed ();
321
0
  env.return_from_subr ();
322
0
  param.set_current_str (env, false);
323
0
  break;
324
325
0
      case OpCode_endchar:
326
0
  param.current_parsed_str->add_op (op, env.str_ref);
327
0
  param.current_parsed_str->set_parsed ();
328
0
  SUPER::process_op (op, env, param);
329
0
  break;
330
331
0
      case OpCode_callsubr:
332
0
  process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
333
0
  break;
334
335
0
      case OpCode_callgsubr:
336
0
  process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
337
0
  break;
338
339
0
      default:
340
0
  SUPER::process_op (op, env, param);
341
0
  param.current_parsed_str->add_op (op, env.str_ref);
342
0
  break;
343
0
    }
344
0
  }
345
346
  protected:
347
  static void process_call_subr (op_code_t op, cs_type_t type,
348
         cff1_cs_interp_env_t &env, subr_subset_param_t& param,
349
         cff1_biased_subrs_t& subrs, hb_set_t *closure)
350
0
  {
351
0
    byte_str_ref_t    str_ref = env.str_ref;
352
0
    env.call_subr (subrs, type);
353
0
    param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
354
0
    closure->add (env.context.subr_num);
355
0
    param.set_current_str (env, true);
356
0
  }
357
358
  private:
359
  typedef cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
360
};
361
362
struct cff1_private_dict_op_serializer_t : op_serializer_t
363
{
364
  cff1_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_)
365
0
    : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
366
367
  bool serialize (hb_serialize_context_t *c,
368
      const op_str_t &opstr,
369
      objidx_t subrs_link) const
370
0
  {
371
0
    TRACE_SERIALIZE (this);
372
373
0
    if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
374
0
      return_trace (true);
375
376
0
    if (opstr.op == OpCode_Subrs)
377
0
    {
378
0
      if (desubroutinize || !subrs_link)
379
0
  return_trace (true);
380
0
      else
381
0
  return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
382
0
    }
383
384
0
    return_trace (copy_opstr (c, opstr));
385
0
  }
386
387
  protected:
388
  const bool desubroutinize;
389
  const bool drop_hints;
390
};
391
392
struct cff1_subr_subsetter_t : subr_subsetter_t<cff1_subr_subsetter_t, CFF1Subrs, const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, OpCode_endchar>
393
{
394
  cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
395
0
    : subr_subsetter_t (acc_, plan_) {}
396
397
  static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
398
0
  {
399
    /* insert width at the beginning of the charstring as necessary */
400
0
    if (env.has_width)
401
0
      charstring.set_prefix (env.width);
402
403
    /* subroutines/charstring left on the call stack are legally left unmarked
404
     * unmarked when a subroutine terminates with endchar. mark them.
405
     */
406
0
    param.current_parsed_str->set_parsed ();
407
0
    for (unsigned int i = 0; i < env.callStack.get_count (); i++)
408
0
    {
409
0
      parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]);
410
0
      if (likely (parsed_str))
411
0
  parsed_str->set_parsed ();
412
0
      else
413
0
  env.set_error ();
414
0
    }
415
0
  }
416
};
417
418
namespace OT {
419
struct cff1_subset_plan
420
{
421
  cff1_subset_plan ()
422
0
  {
423
0
    for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
424
0
      topDictModSIDs[i] = CFF_UNDEF_SID;
425
0
  }
426
427
  void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
428
0
  {
429
0
    const Encoding *encoding = acc.encoding;
430
0
    unsigned int  size0, size1;
431
0
    unsigned code, last_code = CFF_UNDEF_CODE - 1;
432
0
    hb_vector_t<hb_codepoint_t> supp_codes;
433
434
0
    subset_enc_code_ranges.clear ();
435
0
    if (unlikely (subset_enc_code_ranges.in_error ()))
436
0
    {
437
0
      plan->check_success (false);
438
0
      return;
439
0
    }
440
441
0
    supp_codes.init ();
442
443
0
    code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
444
0
    subset_enc_num_codes = plan->num_output_glyphs () - 1;
445
0
    unsigned int glyph;
446
0
    auto it = hb_iter (plan->new_to_old_gid_list);
447
0
    if (it->first == 0) it++;
448
0
    auto _ = *it;
449
0
    for (glyph = 1; glyph < num_glyphs; glyph++)
450
0
    {
451
0
      hb_codepoint_t old_glyph;
452
0
      if (glyph == _.first)
453
0
      {
454
0
  old_glyph = _.second;
455
0
  _ = *++it;
456
0
      }
457
0
      else
458
0
      {
459
  /* Retain the SID for the old missing glyph ID */
460
0
  old_glyph = glyph;
461
0
      }
462
0
      code = acc.glyph_to_code (old_glyph, &glyph_to_sid_cache);
463
0
      if (code == CFF_UNDEF_CODE)
464
0
      {
465
0
  subset_enc_num_codes = glyph - 1;
466
0
  break;
467
0
      }
468
469
0
      if (code != last_code + 1)
470
0
  subset_enc_code_ranges.push (code_pair_t {code, glyph});
471
0
      last_code = code;
472
473
0
      if (encoding != &Null (Encoding))
474
0
      {
475
0
  hb_codepoint_t  sid = acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
476
0
  encoding->get_supplement_codes (sid, supp_codes);
477
0
  for (unsigned int i = 0; i < supp_codes.length; i++)
478
0
    subset_enc_supp_codes.push (code_pair_t {supp_codes[i], sid});
479
0
      }
480
0
    }
481
0
    supp_codes.fini ();
482
483
0
    subset_enc_code_ranges.complete (glyph);
484
485
0
    assert (subset_enc_num_codes <= 0xFF);
486
0
    size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes;
487
0
    size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.length;
488
489
0
    if (size0 < size1)
490
0
      subset_enc_format = 0;
491
0
    else
492
0
      subset_enc_format = 1;
493
0
  }
494
495
  bool plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
496
0
  {
497
0
    unsigned int  size0, size_ranges;
498
0
    unsigned last_sid = CFF_UNDEF_CODE - 1;
499
500
0
    subset_charset_ranges.clear ();
501
0
    if (unlikely (subset_charset_ranges.in_error ()))
502
0
    {
503
0
      plan->check_success (false);
504
0
      return false;
505
0
    }
506
507
0
    code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
508
509
0
    unsigned num_glyphs = plan->num_output_glyphs ();
510
511
0
    if (unlikely (!subset_charset_ranges.alloc (hb_min (num_glyphs,
512
0
              acc.num_charset_entries))))
513
0
    {
514
0
      plan->check_success (false);
515
0
      return false;
516
0
    }
517
518
0
    glyph_to_sid_map_t *glyph_to_sid_map = acc.cff_accelerator ?
519
0
             acc.cff_accelerator->glyph_to_sid_map.get_acquire () :
520
0
             nullptr;
521
522
0
    bool created_map = false;
523
0
    if (!glyph_to_sid_map && acc.cff_accelerator)
524
0
    {
525
0
      created_map = true;
526
0
      glyph_to_sid_map = acc.create_glyph_to_sid_map ();
527
0
    }
528
529
0
    if (!glyph_to_sid_map || glyph_to_sid_map->in_error ())
530
0
    {
531
0
      glyph_to_sid_map = nullptr;
532
0
      created_map = false;
533
0
    }
534
535
0
    auto it = hb_iter (plan->new_to_old_gid_list);
536
0
    if (it->first == 0) it++;
537
0
    auto _ = *it;
538
0
    bool not_is_cid = !acc.is_CID ();
539
0
    bool skip = !not_is_cid && glyph_to_sid_map && !identity_charset;
540
0
    if (not_is_cid)
541
0
      sidmap.alloc (num_glyphs);
542
0
    for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++)
543
0
    {
544
0
      hb_codepoint_t old_glyph;
545
0
      if (glyph == _.first)
546
0
      {
547
0
  old_glyph = _.second;
548
0
  _ = *++it;
549
0
      }
550
0
      else
551
0
      {
552
  /* Retain the SID for the old missing glyph ID */
553
0
  old_glyph = glyph;
554
0
      }
555
0
      unsigned sid;
556
0
      if (identity_charset)
557
0
  sid = glyph;
558
0
      else
559
0
      {
560
0
  sid = glyph_to_sid_map ?
561
0
           glyph_to_sid_map->arrayZ[old_glyph].code :
562
0
           acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
563
564
0
  if (not_is_cid)
565
0
    sid = sidmap.add (sid);
566
0
      }
567
568
0
      if (sid != last_sid + 1)
569
0
  subset_charset_ranges.push (code_pair_t {sid, glyph});
570
571
0
      if (glyph == old_glyph && skip)
572
0
      {
573
0
  glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph);
574
0
  sid += glyph - old_glyph;
575
0
      }
576
0
      last_sid = sid;
577
0
    }
578
579
0
    if (created_map)
580
0
    {
581
0
      if ((!plan->accelerator && acc.cff_accelerator) ||
582
0
    !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
583
0
      {
584
0
  glyph_to_sid_map->~glyph_to_sid_map_t ();
585
0
  hb_free (glyph_to_sid_map);
586
0
      }
587
0
    }
588
589
0
    bool two_byte = subset_charset_ranges.complete (num_glyphs);
590
591
0
    size0 = Charset0::get_size (plan->num_output_glyphs ());
592
0
    if (!two_byte)
593
0
      size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length);
594
0
    else
595
0
      size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length);
596
597
0
    if (size0 < size_ranges)
598
0
      subset_charset_format = 0;
599
0
    else if (!two_byte)
600
0
      subset_charset_format = 1;
601
0
    else
602
0
      subset_charset_format = 2;
603
604
0
    return true;
605
0
  }
606
607
  bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
608
0
  {
609
0
    for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
610
0
    {
611
0
      unsigned int sid = acc.topDict.nameSIDs[i];
612
0
      if (sid != CFF_UNDEF_SID)
613
0
      {
614
0
  topDictModSIDs[i] = sidmap.add (sid);
615
0
      }
616
0
    }
617
618
0
    if (acc.fdArray != &Null (CFF1FDArray))
619
0
      for (unsigned int i = 0; i < orig_fdcount; i++)
620
0
  if (fdmap.has (i))
621
0
    (void)sidmap.add (acc.fontDicts[i].fontName);
622
623
0
    return true;
624
0
  }
625
626
  bool create (const OT::cff1::accelerator_subset_t &acc,
627
         hb_subset_plan_t *plan)
628
0
  {
629
    /* make sure notdef is first */
630
0
    hb_codepoint_t old_glyph;
631
0
    if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
632
633
0
    num_glyphs = plan->num_output_glyphs ();
634
0
    orig_fdcount = acc.fdCount;
635
0
    drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
636
0
    desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE;
637
0
    identity_charset = (plan->flags & HB_SUBSET_FLAGS_CFF_IDENTITY_CHARSET) && acc.is_CID ();
638
639
 #ifdef HB_EXPERIMENTAL_API
640
    min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
641
 #else
642
0
    min_charstrings_off_size = 0;
643
0
 #endif
644
645
0
    subset_charset = !acc.is_predef_charset () || identity_charset;
646
0
    if (!subset_charset)
647
      /* check whether the subset renumbers any glyph IDs */
648
0
      for (const auto &_ : plan->new_to_old_gid_list)
649
0
      {
650
0
  if (_.first != _.second)
651
0
  {
652
0
    subset_charset = true;
653
0
    break;
654
0
  }
655
0
      }
656
657
0
    subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
658
659
    /* top dict INDEX */
660
0
    {
661
      /* Add encoding/charset to a (copy of) top dict as necessary */
662
0
      topdict_mod.init (&acc.topDict);
663
0
      bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
664
0
      bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
665
0
      if (need_to_add_enc || need_to_add_set)
666
0
      {
667
0
  if (need_to_add_enc)
668
0
    topdict_mod.add_op (OpCode_Encoding);
669
0
  if (need_to_add_set)
670
0
    topdict_mod.add_op (OpCode_charset);
671
0
      }
672
0
    }
673
674
    /* Determine re-mapping of font index as fdmap among other info */
675
0
    if (acc.fdSelect != &Null (CFF1FDSelect))
676
0
    {
677
0
  if (unlikely (!hb_plan_subset_cff_fdselect (plan,
678
0
          orig_fdcount,
679
0
          *acc.fdSelect,
680
0
          subset_fdcount,
681
0
          info.fd_select.size,
682
0
          subset_fdselect_format,
683
0
          subset_fdselect_ranges,
684
0
          fdmap)))
685
0
  return false;
686
0
    }
687
0
    else
688
0
      fdmap.identity (1);
689
690
    /* remove unused SIDs & reassign SIDs */
691
0
    {
692
      /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */
693
0
      if (unlikely (!collect_sids_in_dicts (acc)))
694
0
  return false;
695
0
      if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
696
0
  return false;
697
698
0
      if (subset_charset && !plan_subset_charset (acc, plan))
699
0
        return false;
700
701
0
      topdict_mod.reassignSIDs (sidmap);
702
0
    }
703
704
0
    if (desubroutinize)
705
0
    {
706
      /* Flatten global & local subrs */
707
0
      subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, OpCode_endchar>
708
0
        flattener(acc, plan);
709
0
      if (!flattener.flatten (subset_charstrings))
710
0
  return false;
711
0
    }
712
0
    else
713
0
    {
714
0
      cff1_subr_subsetter_t       subr_subsetter (acc, plan);
715
716
      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
717
0
      if (!subr_subsetter.subset ())
718
0
  return false;
719
720
      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
721
0
      if (!subr_subsetter.encode_charstrings (subset_charstrings))
722
0
  return false;
723
724
0
      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
725
0
  return false;
726
727
      /* local subrs */
728
0
      if (!subset_localsubrs.resize (orig_fdcount))
729
0
  return false;
730
0
      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
731
0
      {
732
0
  subset_localsubrs[fd].init ();
733
0
  if (fdmap.has (fd))
734
0
  {
735
0
    if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
736
0
      return false;
737
0
  }
738
0
      }
739
0
    }
740
741
    /* Encoding */
742
0
    if (subset_encoding)
743
0
      plan_subset_encoding (acc, plan);
744
745
    /* private dicts & local subrs */
746
0
    if (!acc.is_CID ())
747
0
      fontdicts_mod.push (cff1_font_dict_values_mod_t ());
748
0
    else
749
0
    {
750
0
      + hb_iter (acc.fontDicts)
751
0
      | hb_filter ([&] (const cff1_font_dict_values_t &_)
752
0
  { return fdmap.has (&_ - &acc.fontDicts[0]); } )
753
0
      | hb_map ([&] (const cff1_font_dict_values_t &_)
754
0
  {
755
0
    cff1_font_dict_values_mod_t mod;
756
0
    mod.init (&_, sidmap[_.fontName]);
757
0
    return mod;
758
0
  })
759
0
      | hb_sink (fontdicts_mod)
760
0
      ;
761
0
    }
762
763
0
    return !plan->in_error () &&
764
0
     (subset_charstrings.length == plan->num_output_glyphs ()) &&
765
0
     (fontdicts_mod.length == subset_fdcount);
766
0
  }
767
768
  cff1_top_dict_values_mod_t  topdict_mod;
769
  cff1_sub_table_info_t   info;
770
771
  unsigned int    num_glyphs;
772
  unsigned int    orig_fdcount = 0;
773
  unsigned int    subset_fdcount = 1;
774
  unsigned int    subset_fdselect_format = 0;
775
  hb_vector_t<code_pair_t>   subset_fdselect_ranges;
776
777
  /* font dict index remap table from fullset FDArray to subset FDArray.
778
   * set to CFF_UNDEF_CODE if excluded from subset */
779
  hb_inc_bimap_t   fdmap;
780
781
  str_buff_vec_t    subset_charstrings;
782
  str_buff_vec_t    subset_globalsubrs;
783
  hb_vector_t<str_buff_vec_t> subset_localsubrs;
784
  hb_vector_t<cff1_font_dict_values_mod_t>  fontdicts_mod;
785
786
  bool    drop_hints = false;
787
788
  bool    gid_renum;
789
  bool    subset_encoding;
790
  uint8_t subset_enc_format;
791
  unsigned int  subset_enc_num_codes;
792
  range_list_t  subset_enc_code_ranges;
793
  hb_vector_t<code_pair_t>  subset_enc_supp_codes;
794
795
  uint8_t subset_charset_format;
796
  range_list_t  subset_charset_ranges;
797
  bool    subset_charset;
798
799
  remap_sid_t sidmap;
800
  unsigned int  topDictModSIDs[name_dict_values_t::ValCount];
801
802
  bool    desubroutinize = false;
803
  bool    identity_charset = false;
804
805
  unsigned  min_charstrings_off_size = 0;
806
};
807
} // namespace OT
808
809
static bool _serialize_cff1_charstrings (hb_serialize_context_t *c,
810
                                         struct OT::cff1_subset_plan &plan,
811
                                         const OT::cff1::accelerator_subset_t  &acc)
812
0
{
813
0
  c->push<CFF1CharStrings> ();
814
815
0
  unsigned data_size = 0;
816
0
  unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
817
0
  if (unlikely (!c->start_zerocopy (total_size)))
818
0
    return false;
819
820
0
  auto *cs = c->start_embed<CFF1CharStrings> ();
821
0
  if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) {
822
0
    c->pop_discard ();
823
0
    return false;
824
0
  }
825
826
0
  plan.info.char_strings_link = c->pop_pack (false);
827
0
  return true;
828
0
}
829
830
bool
831
OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c,
832
             struct OT::cff1_subset_plan &plan) const
833
0
{
834
  /* push charstrings onto the object stack first which will ensure it packs as the last
835
     object in the table. Keeping the chastrings last satisfies the requirements for patching
836
     via IFTB. If this ordering needs to be changed in the future, charstrings should be left
837
     at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
838
0
  if (!_serialize_cff1_charstrings(c, plan, *this))
839
0
    return false;
840
841
  /* private dicts & local subrs */
842
0
  for (int i = (int) privateDicts.length; --i >= 0 ;)
843
0
  {
844
0
    if (plan.fdmap.has (i))
845
0
    {
846
0
      objidx_t  subrs_link = 0;
847
0
      if (plan.subset_localsubrs[i].length > 0)
848
0
      {
849
0
  auto *dest = c->push <CFF1Subrs> ();
850
0
  if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
851
0
    subrs_link = c->pop_pack ();
852
0
  else
853
0
  {
854
0
    c->pop_discard ();
855
0
    return false;
856
0
  }
857
0
      }
858
859
0
      auto *pd = c->push<PrivateDict> ();
860
0
      cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
861
      /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
862
0
      if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
863
0
      {
864
0
  unsigned fd = plan.fdmap[i];
865
0
  plan.fontdicts_mod[fd].privateDictInfo.size = c->length ();
866
0
  plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack ();
867
0
      }
868
0
      else
869
0
      {
870
0
  c->pop_discard ();
871
0
  return false;
872
0
      }
873
0
    }
874
0
  }
875
876
0
  if (!is_CID ())
877
0
    plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
878
879
  /* FDArray (FD Index) */
880
0
  if (fdArray != &Null (CFF1FDArray))
881
0
  {
882
0
    auto *fda = c->push<CFF1FDArray> ();
883
0
    cff1_font_dict_op_serializer_t  fontSzr;
884
0
    auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod));
885
0
    if (likely (fda->serialize (c, it, fontSzr)))
886
0
      plan.info.fd_array_link = c->pop_pack (false);
887
0
    else
888
0
    {
889
0
      c->pop_discard ();
890
0
      return false;
891
0
    }
892
0
  }
893
894
  /* FDSelect */
895
0
  if (fdSelect != &Null (CFF1FDSelect))
896
0
  {
897
0
    c->push ();
898
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount,
899
0
             plan.subset_fdselect_format, plan.info.fd_select.size,
900
0
             plan.subset_fdselect_ranges)))
901
0
      plan.info.fd_select.link = c->pop_pack ();
902
0
    else
903
0
    {
904
0
      c->pop_discard ();
905
0
      return false;
906
0
    }
907
0
  }
908
909
  /* Charset */
910
0
  if (plan.subset_charset)
911
0
  {
912
0
    auto *dest = c->push<Charset> ();
913
0
    if (likely (dest->serialize (c,
914
0
         plan.subset_charset_format,
915
0
         plan.num_glyphs,
916
0
         plan.subset_charset_ranges)))
917
0
      plan.info.charset_link = c->pop_pack ();
918
0
    else
919
0
    {
920
0
      c->pop_discard ();
921
0
      return false;
922
0
    }
923
0
  }
924
925
  /* Encoding */
926
0
  if (plan.subset_encoding)
927
0
  {
928
0
    auto *dest = c->push<Encoding> ();
929
0
    if (likely (dest->serialize (c,
930
0
         plan.subset_enc_format,
931
0
         plan.subset_enc_num_codes,
932
0
         plan.subset_enc_code_ranges,
933
0
         plan.subset_enc_supp_codes)))
934
0
      plan.info.encoding_link = c->pop_pack ();
935
0
    else
936
0
    {
937
0
      c->pop_discard ();
938
0
      return false;
939
0
    }
940
0
  }
941
942
  /* global subrs */
943
0
  {
944
0
    auto *dest = c->push <CFF1Subrs> ();
945
0
    if (likely (dest->serialize (c, plan.subset_globalsubrs)))
946
0
      c->pop_pack (false);
947
0
    else
948
0
    {
949
0
      c->pop_discard ();
950
0
      return false;
951
0
    }
952
0
  }
953
954
  /* String INDEX */
955
0
  {
956
0
    auto *dest = c->push<CFF1StringIndex> ();
957
0
    if (likely (!plan.sidmap.in_error () &&
958
0
    dest->serialize (c, *stringIndex, plan.sidmap.vector)))
959
0
      c->pop_pack ();
960
0
    else
961
0
    {
962
0
      c->pop_discard ();
963
0
      return false;
964
0
    }
965
0
  }
966
967
0
  OT::cff1 *cff = c->allocate_min<OT::cff1> ();
968
0
  if (unlikely (!cff))
969
0
    return false;
970
971
  /* header */
972
0
  cff->version.major = 0x01;
973
0
  cff->version.minor = 0x00;
974
0
  cff->nameIndex = cff->min_size;
975
0
  cff->offSize = 4; /* unused? */
976
977
  /* name INDEX */
978
0
  if (unlikely (!c->embed (*nameIndex))) return false;
979
980
  /* top dict INDEX */
981
0
  {
982
    /* serialize singleton TopDict */
983
0
    auto *top = c->push<TopDict> ();
984
0
    cff1_top_dict_op_serializer_t topSzr;
985
0
    unsigned top_size = 0;
986
0
    top_dict_modifiers_t  modifier (plan.info, plan.topDictModSIDs);
987
0
    if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier)))
988
0
    {
989
0
      top_size = c->length ();
990
0
      c->pop_pack (false);
991
0
    }
992
0
    else
993
0
    {
994
0
      c->pop_discard ();
995
0
      return false;
996
0
    }
997
    /* serialize INDEX header for above */
998
0
    auto *dest = c->start_embed<CFF1Index> ();
999
0
    return dest->serialize_header (c, hb_iter (&top_size, 1), top_size);
1000
0
  }
1001
0
}
1002
1003
bool
1004
OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const
1005
0
{
1006
0
  cff1_subset_plan cff_plan;
1007
1008
0
  if (unlikely (!cff_plan.create (*this, c->plan)))
1009
0
  {
1010
0
    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
1011
0
    return false;
1012
0
  }
1013
1014
0
  return serialize (c->serializer, cff_plan);
1015
0
}
1016
1017
1018
#endif