Coverage Report

Created: 2026-03-31 11:00

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