Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/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
0
    bool created_map = false;
522
0
    if (!glyph_to_sid_map && acc.cff_accelerator)
523
0
    {
524
0
      created_map = true;
525
0
      glyph_to_sid_map = acc.create_glyph_to_sid_map ();
526
0
    }
527
528
0
    auto it = hb_iter (plan->new_to_old_gid_list);
529
0
    if (it->first == 0) it++;
530
0
    auto _ = *it;
531
0
    bool not_is_cid = !acc.is_CID ();
532
0
    bool skip = !not_is_cid && glyph_to_sid_map;
533
0
    if (not_is_cid)
534
0
      sidmap.alloc (num_glyphs);
535
0
    for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++)
536
0
    {
537
0
      hb_codepoint_t old_glyph;
538
0
      if (glyph == _.first)
539
0
      {
540
0
  old_glyph = _.second;
541
0
  _ = *++it;
542
0
      }
543
0
      else
544
0
      {
545
  /* Retain the SID for the old missing glyph ID */
546
0
  old_glyph = glyph;
547
0
      }
548
0
      unsigned sid = glyph_to_sid_map ?
549
0
         glyph_to_sid_map->arrayZ[old_glyph].code :
550
0
         acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
551
552
0
      if (not_is_cid)
553
0
  sid = sidmap.add (sid);
554
555
0
      if (sid != last_sid + 1)
556
0
  subset_charset_ranges.push (code_pair_t {sid, glyph});
557
558
0
      if (glyph == old_glyph && skip)
559
0
      {
560
0
  glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph);
561
0
  sid += glyph - old_glyph;
562
0
      }
563
0
      last_sid = sid;
564
0
    }
565
566
0
    if (created_map)
567
0
    {
568
0
      if ((!plan->accelerator && acc.cff_accelerator) ||
569
0
    !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
570
0
      {
571
0
  glyph_to_sid_map->~glyph_to_sid_map_t ();
572
0
  hb_free (glyph_to_sid_map);
573
0
      }
574
0
    }
575
576
0
    bool two_byte = subset_charset_ranges.complete (num_glyphs);
577
578
0
    size0 = Charset0::get_size (plan->num_output_glyphs ());
579
0
    if (!two_byte)
580
0
      size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length);
581
0
    else
582
0
      size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length);
583
584
0
    if (size0 < size_ranges)
585
0
      subset_charset_format = 0;
586
0
    else if (!two_byte)
587
0
      subset_charset_format = 1;
588
0
    else
589
0
      subset_charset_format = 2;
590
591
0
    return true;
592
0
  }
593
594
  bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
595
0
  {
596
0
    for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
597
0
    {
598
0
      unsigned int sid = acc.topDict.nameSIDs[i];
599
0
      if (sid != CFF_UNDEF_SID)
600
0
      {
601
0
  topDictModSIDs[i] = sidmap.add (sid);
602
0
      }
603
0
    }
604
605
0
    if (acc.fdArray != &Null (CFF1FDArray))
606
0
      for (unsigned int i = 0; i < orig_fdcount; i++)
607
0
  if (fdmap.has (i))
608
0
    (void)sidmap.add (acc.fontDicts[i].fontName);
609
610
0
    return true;
611
0
  }
612
613
  bool create (const OT::cff1::accelerator_subset_t &acc,
614
         hb_subset_plan_t *plan)
615
0
  {
616
    /* make sure notdef is first */
617
0
    hb_codepoint_t old_glyph;
618
0
    if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
619
620
0
    num_glyphs = plan->num_output_glyphs ();
621
0
    orig_fdcount = acc.fdCount;
622
0
    drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
623
0
    desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE;
624
625
 #ifdef HB_EXPERIMENTAL_API
626
    min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
627
 #else
628
0
    min_charstrings_off_size = 0;
629
0
 #endif
630
631
0
    subset_charset = !acc.is_predef_charset ();
632
0
    if (!subset_charset)
633
      /* check whether the subset renumbers any glyph IDs */
634
0
      for (const auto &_ : plan->new_to_old_gid_list)
635
0
      {
636
0
  if (_.first != _.second)
637
0
  {
638
0
    subset_charset = true;
639
0
    break;
640
0
  }
641
0
      }
642
643
0
    subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
644
645
    /* top dict INDEX */
646
0
    {
647
      /* Add encoding/charset to a (copy of) top dict as necessary */
648
0
      topdict_mod.init (&acc.topDict);
649
0
      bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
650
0
      bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
651
0
      if (need_to_add_enc || need_to_add_set)
652
0
      {
653
0
  if (need_to_add_enc)
654
0
    topdict_mod.add_op (OpCode_Encoding);
655
0
  if (need_to_add_set)
656
0
    topdict_mod.add_op (OpCode_charset);
657
0
      }
658
0
    }
659
660
    /* Determine re-mapping of font index as fdmap among other info */
661
0
    if (acc.fdSelect != &Null (CFF1FDSelect))
662
0
    {
663
0
  if (unlikely (!hb_plan_subset_cff_fdselect (plan,
664
0
          orig_fdcount,
665
0
          *acc.fdSelect,
666
0
          subset_fdcount,
667
0
          info.fd_select.size,
668
0
          subset_fdselect_format,
669
0
          subset_fdselect_ranges,
670
0
          fdmap)))
671
0
  return false;
672
0
    }
673
0
    else
674
0
      fdmap.identity (1);
675
676
    /* remove unused SIDs & reassign SIDs */
677
0
    {
678
      /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */
679
0
      if (unlikely (!collect_sids_in_dicts (acc)))
680
0
  return false;
681
0
      if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
682
0
  return false;
683
684
0
      if (subset_charset && !plan_subset_charset (acc, plan))
685
0
        return false;
686
687
0
      topdict_mod.reassignSIDs (sidmap);
688
0
    }
689
690
0
    if (desubroutinize)
691
0
    {
692
      /* Flatten global & local subrs */
693
0
      subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, OpCode_endchar>
694
0
        flattener(acc, plan);
695
0
      if (!flattener.flatten (subset_charstrings))
696
0
  return false;
697
0
    }
698
0
    else
699
0
    {
700
0
      cff1_subr_subsetter_t       subr_subsetter (acc, plan);
701
702
      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
703
0
      if (!subr_subsetter.subset ())
704
0
  return false;
705
706
      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
707
0
      if (!subr_subsetter.encode_charstrings (subset_charstrings))
708
0
  return false;
709
710
0
      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
711
0
  return false;
712
713
      /* local subrs */
714
0
      if (!subset_localsubrs.resize (orig_fdcount))
715
0
  return false;
716
0
      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
717
0
      {
718
0
  subset_localsubrs[fd].init ();
719
0
  if (fdmap.has (fd))
720
0
  {
721
0
    if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
722
0
      return false;
723
0
  }
724
0
      }
725
0
    }
726
727
    /* Encoding */
728
0
    if (subset_encoding)
729
0
      plan_subset_encoding (acc, plan);
730
731
    /* private dicts & local subrs */
732
0
    if (!acc.is_CID ())
733
0
      fontdicts_mod.push (cff1_font_dict_values_mod_t ());
734
0
    else
735
0
    {
736
0
      + hb_iter (acc.fontDicts)
737
0
      | hb_filter ([&] (const cff1_font_dict_values_t &_)
738
0
  { return fdmap.has (&_ - &acc.fontDicts[0]); } )
739
0
      | hb_map ([&] (const cff1_font_dict_values_t &_)
740
0
  {
741
0
    cff1_font_dict_values_mod_t mod;
742
0
    mod.init (&_, sidmap[_.fontName]);
743
0
    return mod;
744
0
  })
745
0
      | hb_sink (fontdicts_mod)
746
0
      ;
747
0
    }
748
749
0
    return !plan->in_error () &&
750
0
     (subset_charstrings.length == plan->num_output_glyphs ()) &&
751
0
     (fontdicts_mod.length == subset_fdcount);
752
0
  }
753
754
  cff1_top_dict_values_mod_t  topdict_mod;
755
  cff1_sub_table_info_t   info;
756
757
  unsigned int    num_glyphs;
758
  unsigned int    orig_fdcount = 0;
759
  unsigned int    subset_fdcount = 1;
760
  unsigned int    subset_fdselect_format = 0;
761
  hb_vector_t<code_pair_t>   subset_fdselect_ranges;
762
763
  /* font dict index remap table from fullset FDArray to subset FDArray.
764
   * set to CFF_UNDEF_CODE if excluded from subset */
765
  hb_inc_bimap_t   fdmap;
766
767
  str_buff_vec_t    subset_charstrings;
768
  str_buff_vec_t    subset_globalsubrs;
769
  hb_vector_t<str_buff_vec_t> subset_localsubrs;
770
  hb_vector_t<cff1_font_dict_values_mod_t>  fontdicts_mod;
771
772
  bool    drop_hints = false;
773
774
  bool    gid_renum;
775
  bool    subset_encoding;
776
  uint8_t subset_enc_format;
777
  unsigned int  subset_enc_num_codes;
778
  range_list_t  subset_enc_code_ranges;
779
  hb_vector_t<code_pair_t>  subset_enc_supp_codes;
780
781
  uint8_t subset_charset_format;
782
  range_list_t  subset_charset_ranges;
783
  bool    subset_charset;
784
785
  remap_sid_t sidmap;
786
  unsigned int  topDictModSIDs[name_dict_values_t::ValCount];
787
788
  bool    desubroutinize = false;
789
790
  unsigned  min_charstrings_off_size = 0;
791
};
792
} // namespace OT
793
794
static bool _serialize_cff1_charstrings (hb_serialize_context_t *c,
795
                                         struct OT::cff1_subset_plan &plan,
796
                                         const OT::cff1::accelerator_subset_t  &acc)
797
0
{
798
0
  c->push<CFF1CharStrings> ();
799
800
0
  unsigned data_size = 0;
801
0
  unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
802
0
  if (unlikely (!c->start_zerocopy (total_size)))
803
0
    return false;
804
805
0
  auto *cs = c->start_embed<CFF1CharStrings> ();
806
0
  if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) {
807
0
    c->pop_discard ();
808
0
    return false;
809
0
  }
810
811
0
  plan.info.char_strings_link = c->pop_pack (false);
812
0
  return true;
813
0
}
814
815
bool
816
OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c,
817
             struct OT::cff1_subset_plan &plan) const
818
0
{
819
  /* push charstrings onto the object stack first which will ensure it packs as the last
820
     object in the table. Keeping the chastrings last satisfies the requirements for patching
821
     via IFTB. If this ordering needs to be changed in the future, charstrings should be left
822
     at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
823
0
  if (!_serialize_cff1_charstrings(c, plan, *this))
824
0
    return false;
825
826
  /* private dicts & local subrs */
827
0
  for (int i = (int) privateDicts.length; --i >= 0 ;)
828
0
  {
829
0
    if (plan.fdmap.has (i))
830
0
    {
831
0
      objidx_t  subrs_link = 0;
832
0
      if (plan.subset_localsubrs[i].length > 0)
833
0
      {
834
0
  auto *dest = c->push <CFF1Subrs> ();
835
0
  if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
836
0
    subrs_link = c->pop_pack ();
837
0
  else
838
0
  {
839
0
    c->pop_discard ();
840
0
    return false;
841
0
  }
842
0
      }
843
844
0
      auto *pd = c->push<PrivateDict> ();
845
0
      cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
846
      /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
847
0
      if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
848
0
      {
849
0
  unsigned fd = plan.fdmap[i];
850
0
  plan.fontdicts_mod[fd].privateDictInfo.size = c->length ();
851
0
  plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack ();
852
0
      }
853
0
      else
854
0
      {
855
0
  c->pop_discard ();
856
0
  return false;
857
0
      }
858
0
    }
859
0
  }
860
861
0
  if (!is_CID ())
862
0
    plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
863
864
  /* FDArray (FD Index) */
865
0
  if (fdArray != &Null (CFF1FDArray))
866
0
  {
867
0
    auto *fda = c->push<CFF1FDArray> ();
868
0
    cff1_font_dict_op_serializer_t  fontSzr;
869
0
    auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod));
870
0
    if (likely (fda->serialize (c, it, fontSzr)))
871
0
      plan.info.fd_array_link = c->pop_pack (false);
872
0
    else
873
0
    {
874
0
      c->pop_discard ();
875
0
      return false;
876
0
    }
877
0
  }
878
879
  /* FDSelect */
880
0
  if (fdSelect != &Null (CFF1FDSelect))
881
0
  {
882
0
    c->push ();
883
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount,
884
0
             plan.subset_fdselect_format, plan.info.fd_select.size,
885
0
             plan.subset_fdselect_ranges)))
886
0
      plan.info.fd_select.link = c->pop_pack ();
887
0
    else
888
0
    {
889
0
      c->pop_discard ();
890
0
      return false;
891
0
    }
892
0
  }
893
894
  /* Charset */
895
0
  if (plan.subset_charset)
896
0
  {
897
0
    auto *dest = c->push<Charset> ();
898
0
    if (likely (dest->serialize (c,
899
0
         plan.subset_charset_format,
900
0
         plan.num_glyphs,
901
0
         plan.subset_charset_ranges)))
902
0
      plan.info.charset_link = c->pop_pack ();
903
0
    else
904
0
    {
905
0
      c->pop_discard ();
906
0
      return false;
907
0
    }
908
0
  }
909
910
  /* Encoding */
911
0
  if (plan.subset_encoding)
912
0
  {
913
0
    auto *dest = c->push<Encoding> ();
914
0
    if (likely (dest->serialize (c,
915
0
         plan.subset_enc_format,
916
0
         plan.subset_enc_num_codes,
917
0
         plan.subset_enc_code_ranges,
918
0
         plan.subset_enc_supp_codes)))
919
0
      plan.info.encoding_link = c->pop_pack ();
920
0
    else
921
0
    {
922
0
      c->pop_discard ();
923
0
      return false;
924
0
    }
925
0
  }
926
927
  /* global subrs */
928
0
  {
929
0
    auto *dest = c->push <CFF1Subrs> ();
930
0
    if (likely (dest->serialize (c, plan.subset_globalsubrs)))
931
0
      c->pop_pack (false);
932
0
    else
933
0
    {
934
0
      c->pop_discard ();
935
0
      return false;
936
0
    }
937
0
  }
938
939
  /* String INDEX */
940
0
  {
941
0
    auto *dest = c->push<CFF1StringIndex> ();
942
0
    if (likely (!plan.sidmap.in_error () &&
943
0
    dest->serialize (c, *stringIndex, plan.sidmap.vector)))
944
0
      c->pop_pack ();
945
0
    else
946
0
    {
947
0
      c->pop_discard ();
948
0
      return false;
949
0
    }
950
0
  }
951
952
0
  OT::cff1 *cff = c->allocate_min<OT::cff1> ();
953
0
  if (unlikely (!cff))
954
0
    return false;
955
956
  /* header */
957
0
  cff->version.major = 0x01;
958
0
  cff->version.minor = 0x00;
959
0
  cff->nameIndex = cff->min_size;
960
0
  cff->offSize = 4; /* unused? */
961
962
  /* name INDEX */
963
0
  if (unlikely (!c->embed (*nameIndex))) return false;
964
965
  /* top dict INDEX */
966
0
  {
967
    /* serialize singleton TopDict */
968
0
    auto *top = c->push<TopDict> ();
969
0
    cff1_top_dict_op_serializer_t topSzr;
970
0
    unsigned top_size = 0;
971
0
    top_dict_modifiers_t  modifier (plan.info, plan.topDictModSIDs);
972
0
    if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier)))
973
0
    {
974
0
      top_size = c->length ();
975
0
      c->pop_pack (false);
976
0
    }
977
0
    else
978
0
    {
979
0
      c->pop_discard ();
980
0
      return false;
981
0
    }
982
    /* serialize INDEX header for above */
983
0
    auto *dest = c->start_embed<CFF1Index> ();
984
0
    return dest->serialize_header (c, hb_iter (&top_size, 1), top_size);
985
0
  }
986
0
}
987
988
bool
989
OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const
990
0
{
991
0
  cff1_subset_plan cff_plan;
992
993
0
  if (unlikely (!cff_plan.create (*this, c->plan)))
994
0
  {
995
0
    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
996
0
    return false;
997
0
  }
998
999
0
  return serialize (c->serializer, cff_plan);
1000
0
}
1001
1002
1003
#endif