Coverage Report

Created: 2026-11-06 11:29

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