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-cff2.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-cff2-table.hh"
33
#include "hb-set.h"
34
#include "hb-subset-plan.hh"
35
#include "hb-subset-cff-common.hh"
36
#include "hb-cff2-interp-cs.hh"
37
#include "hb-subset-cff2-to-cff1.hh"
38
39
using namespace CFF;
40
41
struct cff2_sub_table_info_t : cff_sub_table_info_t
42
{
43
  cff2_sub_table_info_t ()
44
0
    : cff_sub_table_info_t (),
45
0
      var_store_link (0)
46
0
  {}
47
48
  objidx_t  var_store_link;
49
};
50
51
struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
52
{
53
  bool serialize (hb_serialize_context_t *c,
54
      const op_str_t &opstr,
55
      const cff2_sub_table_info_t &info) const
56
0
  {
57
0
    TRACE_SERIALIZE (this);
58
59
0
    switch (opstr.op)
60
0
    {
61
0
      case OpCode_vstore:
62
0
        if (info.var_store_link)
63
0
    return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
64
0
  else
65
0
    return_trace (true);
66
67
0
      default:
68
0
  return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info));
69
0
    }
70
0
  }
71
};
72
73
struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
74
{
75
  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
76
0
  {
77
    /* Optionally capture command for specialization (before flushing, to preserve args) */
78
0
    if (param.commands)
79
0
    {
80
0
      bool skip_command = false;
81
82
0
      switch (op)
83
0
      {
84
0
  case OpCode_return:
85
0
  case OpCode_endchar:
86
0
    skip_command = true;
87
0
    break;
88
89
0
  case OpCode_hstem:
90
0
  case OpCode_hstemhm:
91
0
  case OpCode_vstem:
92
0
  case OpCode_vstemhm:
93
0
  case OpCode_hintmask:
94
0
  case OpCode_cntrmask:
95
0
    if (param.drop_hints)
96
0
      skip_command = true;
97
0
    break;
98
99
0
  default:
100
0
    break;
101
0
      }
102
103
0
      if (!skip_command)
104
0
      {
105
0
  cs_command_t cmd (op);
106
  /* Capture resolved blend values */
107
0
  for (unsigned int i = 0; i < env.argStack.get_count ();)
108
0
  {
109
0
    const blend_arg_t &arg = env.argStack[i];
110
0
    if (arg.blending ())
111
0
    {
112
      /* For blend args, capture only the resolved default value */
113
0
      cmd.args.push (arg);
114
      /* Skip over the multiple blend values */
115
0
      i += arg.numValues;
116
0
    }
117
0
    else
118
0
    {
119
0
      cmd.args.push (arg);
120
0
      i++;
121
0
    }
122
0
  }
123
0
  param.commands->push (cmd);
124
0
      }
125
0
    }
126
127
0
    switch (op)
128
0
    {
129
0
      case OpCode_return:
130
0
      case OpCode_endchar:
131
  /* dummy opcodes in CFF2. ignore */
132
0
  break;
133
134
0
      case OpCode_hstem:
135
0
      case OpCode_hstemhm:
136
0
      case OpCode_vstem:
137
0
      case OpCode_vstemhm:
138
0
      case OpCode_hintmask:
139
0
      case OpCode_cntrmask:
140
0
  if (param.drop_hints)
141
0
  {
142
0
    env.clear_args ();
143
0
    return;
144
0
  }
145
0
  HB_FALLTHROUGH;
146
147
0
      default:
148
0
  SUPER::flush_args_and_op (op, env, param);
149
0
  break;
150
0
    }
151
0
  }
152
153
  static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
154
0
  {
155
0
    for (unsigned int i = 0; i < env.argStack.get_count ();)
156
0
    {
157
0
      const blend_arg_t &arg = env.argStack[i];
158
0
      if (arg.blending ())
159
0
      {
160
0
  if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
161
0
  {
162
0
    env.set_error ();
163
0
    return;
164
0
  }
165
0
  flatten_blends (arg, i, env, param);
166
0
  i += arg.numValues;
167
0
      }
168
0
      else
169
0
      {
170
0
  str_encoder_t  encoder (param.flatStr);
171
0
  encoder.encode_num_cs (arg);
172
0
  i++;
173
0
      }
174
0
    }
175
0
    SUPER::flush_args (env, param);
176
0
  }
177
178
  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
179
0
  {
180
    /* flatten the default values */
181
0
    str_encoder_t  encoder (param.flatStr);
182
0
    for (unsigned int j = 0; j < arg.numValues; j++)
183
0
    {
184
0
      const blend_arg_t &arg1 = env.argStack[i + j];
185
0
      if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
186
0
        (arg1.deltas.length == env.get_region_count ())))))
187
0
      {
188
0
  env.set_error ();
189
0
  return;
190
0
      }
191
0
      encoder.encode_num_cs (arg1);
192
0
    }
193
    /* flatten deltas for each value */
194
0
    for (unsigned int j = 0; j < arg.numValues; j++)
195
0
    {
196
0
      const blend_arg_t &arg1 = env.argStack[i + j];
197
0
      for (unsigned int k = 0; k < arg1.deltas.length; k++)
198
0
  encoder.encode_num_cs (arg1.deltas[k]);
199
0
    }
200
    /* flatten the number of values followed by blend operator */
201
0
    encoder.encode_int (arg.numValues);
202
0
    encoder.encode_op (OpCode_blendcs);
203
0
  }
204
205
  static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
206
0
  {
207
0
    switch (op)
208
0
    {
209
0
      case OpCode_return:
210
0
      case OpCode_endchar:
211
0
  return;
212
0
      default:
213
0
  str_encoder_t  encoder (param.flatStr);
214
0
  encoder.encode_op (op);
215
0
    }
216
0
  }
217
218
  static void flush_hintmask (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
219
0
  {
220
0
    SUPER::flush_hintmask (op, env, param);
221
    /* Preserve hintmask payload in captured commands for specializer re-encoding. */
222
0
    if (param.commands && !param.drop_hints && param.commands->length > 0)
223
0
    {
224
0
      auto &cmd = param.commands->tail ();
225
0
      if (cmd.op == op)
226
0
      {
227
0
        cmd.mask_bytes.resize (env.hintmask_size);
228
0
        if (unlikely (cmd.mask_bytes.in_error ()))
229
0
        {
230
0
          env.set_error ();
231
0
          return;
232
0
        }
233
0
        for (unsigned int i = 0; i < env.hintmask_size; i++)
234
0
          cmd.mask_bytes[i] = env.str_ref[i];
235
0
      }
236
0
    }
237
0
    if (!param.drop_hints)
238
0
    {
239
0
      str_encoder_t  encoder (param.flatStr);
240
0
      for (unsigned int i = 0; i < env.hintmask_size; i++)
241
0
  encoder.encode_byte (env.str_ref[i]);
242
0
    }
243
0
  }
244
245
  private:
246
  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
247
  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET;
248
};
249
250
struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
251
{
252
  static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
253
0
  {
254
0
    switch (op) {
255
256
0
      case OpCode_return:
257
0
  param.current_parsed_str->set_parsed ();
258
0
  env.return_from_subr ();
259
0
  param.set_current_str (env, false);
260
0
  break;
261
262
0
      case OpCode_endchar:
263
0
  param.current_parsed_str->set_parsed ();
264
0
  SUPER::process_op (op, env, param);
265
0
  break;
266
267
0
      case OpCode_callsubr:
268
0
  process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
269
0
  break;
270
271
0
      case OpCode_callgsubr:
272
0
  process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
273
0
  break;
274
275
0
      default:
276
0
  SUPER::process_op (op, env, param);
277
0
  param.current_parsed_str->add_op (op, env.str_ref);
278
0
  break;
279
0
    }
280
0
  }
281
282
  protected:
283
  static void process_call_subr (op_code_t op, cs_type_t type,
284
         cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
285
         cff2_biased_subrs_t& subrs, hb_set_t *closure)
286
0
  {
287
0
    byte_str_ref_t    str_ref = env.str_ref;
288
0
    env.call_subr (subrs, type);
289
0
    param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
290
0
    closure->add (env.context.subr_num);
291
0
    param.set_current_str (env, true);
292
0
  }
293
294
  private:
295
  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
296
};
297
298
struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t>
299
{
300
  cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
301
0
    : subr_subsetter_t (acc_, plan_) {}
302
303
  static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
304
0
  {
305
    /* vsindex is inserted at the beginning of the charstring as necessary */
306
0
    if (env.seen_vsindex ())
307
0
    {
308
0
      number_t  ivs;
309
0
      ivs.set_int ((int)env.get_ivs ());
310
0
      charstring.set_prefix (ivs, OpCode_vsindexcs);
311
0
    }
312
0
  }
313
};
314
315
struct cff2_private_blend_encoder_param_t
316
{
317
  cff2_private_blend_encoder_param_t (hb_serialize_context_t *c,
318
              const CFF2ItemVariationStore *varStore,
319
              hb_array_t<int> normalized_coords) :
320
0
    c (c), varStore (varStore), normalized_coords (normalized_coords) {}
321
322
0
  void init () {}
323
324
  void process_blend ()
325
0
  {
326
0
    if (!seen_blend)
327
0
    {
328
0
      region_count = varStore->varStore.get_region_index_count (ivs);
329
0
      scalars.resize_exact (region_count);
330
0
      varStore->varStore.get_region_scalars (ivs, normalized_coords.arrayZ, normalized_coords.length,
331
0
               &scalars[0], region_count);
332
0
      seen_blend = true;
333
0
    }
334
0
  }
335
336
  double blend_deltas (hb_array_t<const number_t> deltas) const
337
0
  {
338
0
    double v = 0;
339
0
    if (likely (scalars.length == deltas.length))
340
0
    {
341
0
      unsigned count = scalars.length;
342
0
      for (unsigned i = 0; i < count; i++)
343
0
  v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
344
0
    }
345
0
    return v;
346
0
  }
347
348
349
  hb_serialize_context_t *c = nullptr;
350
  bool seen_blend = false;
351
  unsigned ivs = 0;
352
  unsigned region_count = 0;
353
  hb_vector_t<float> scalars;
354
  const  CFF2ItemVariationStore *varStore = nullptr;
355
  hb_array_t<int> normalized_coords;
356
};
357
358
struct cff2_private_dict_blend_opset_t : dict_opset_t
359
{
360
  static void process_arg_blend (cff2_private_blend_encoder_param_t& param,
361
         number_t &arg,
362
         const hb_array_t<const number_t> blends,
363
         unsigned n, unsigned i)
364
0
  {
365
0
    arg.set_int (round (arg.to_real () + param.blend_deltas (blends)));
366
0
  }
367
368
  static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
369
0
  {
370
0
    unsigned int n, k;
371
372
0
    param.process_blend ();
373
0
    k = param.region_count;
374
0
    n = env.argStack.pop_uint ();
375
    /* copy the blend values into blend array of the default values */
376
0
    unsigned int start = env.argStack.get_count () - ((k+1) * n);
377
    /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
378
0
    if (unlikely (start > env.argStack.get_count ()))
379
0
    {
380
0
      env.set_error ();
381
0
      return;
382
0
    }
383
0
    for (unsigned int i = 0; i < n; i++)
384
0
    {
385
0
      const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (i * k), k);
386
0
      process_arg_blend (param, env.argStack[start + i], blends, n, i);
387
0
    }
388
389
    /* pop off blend values leaving default values now adorned with blend values */
390
0
    env.argStack.pop (k * n);
391
0
  }
392
393
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
394
0
  {
395
0
    switch (op) {
396
0
      case OpCode_StdHW:
397
0
      case OpCode_StdVW:
398
0
      case OpCode_BlueScale:
399
0
      case OpCode_BlueShift:
400
0
      case OpCode_BlueFuzz:
401
0
      case OpCode_ExpansionFactor:
402
0
      case OpCode_LanguageGroup:
403
0
      case OpCode_BlueValues:
404
0
      case OpCode_OtherBlues:
405
0
      case OpCode_FamilyBlues:
406
0
      case OpCode_FamilyOtherBlues:
407
0
      case OpCode_StemSnapH:
408
0
      case OpCode_StemSnapV:
409
0
  break;
410
0
      case OpCode_vsindexdict:
411
0
  env.process_vsindex ();
412
0
  param.ivs = env.get_ivs ();
413
0
  env.clear_args ();
414
0
  return;
415
0
      case OpCode_blenddict:
416
0
  process_blend (env, param);
417
0
  return;
418
419
0
      default:
420
0
  dict_opset_t::process_op (op, env);
421
0
  if (!env.argStack.is_empty ()) return;
422
0
  break;
423
0
    }
424
425
0
    if (unlikely (env.in_error ())) return;
426
427
    // Write args then op
428
429
0
    str_buff_t str;
430
0
    str_encoder_t encoder (str);
431
432
0
    unsigned count = env.argStack.get_count ();
433
0
    for (unsigned i = 0; i < count; i++)
434
0
      encoder.encode_num_tp (env.argStack[i]);
435
436
0
    encoder.encode_op (op);
437
438
0
    auto bytes = str.as_bytes ();
439
0
    param.c->embed (&bytes, bytes.length);
440
441
0
    env.clear_args ();
442
0
  }
443
};
444
445
struct cff2_private_dict_op_serializer_t : op_serializer_t
446
{
447
  cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_,
448
             const CFF::CFF2ItemVariationStore* varStore_,
449
             hb_array_t<int> normalized_coords_)
450
0
    : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_),
451
0
      varStore (varStore_), normalized_coords (normalized_coords_) {}
452
453
  bool serialize (hb_serialize_context_t *c,
454
      const op_str_t &opstr,
455
      objidx_t subrs_link) const
456
0
  {
457
0
    TRACE_SERIALIZE (this);
458
459
0
    if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
460
0
      return_trace (true);
461
462
0
    if (opstr.op == OpCode_Subrs)
463
0
    {
464
0
      if (desubroutinize || !subrs_link)
465
0
  return_trace (true);
466
0
      else
467
0
  return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
468
0
    }
469
470
0
    if (pinned)
471
0
    {
472
      // Reinterpret opstr and process blends.
473
0
      cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)};
474
0
      cff2_private_blend_encoder_param_t param (c, varStore, normalized_coords);
475
0
      dict_interpreter_t<cff2_private_dict_blend_opset_t, cff2_private_blend_encoder_param_t, cff2_priv_dict_interp_env_t> interp (env);
476
0
      return_trace (interp.interpret (param));
477
0
    }
478
479
0
    return_trace (copy_opstr (c, opstr));
480
0
  }
481
482
  protected:
483
  const bool desubroutinize;
484
  const bool drop_hints;
485
  const bool pinned;
486
  const CFF::CFF2ItemVariationStore* varStore;
487
  hb_array_t<int> normalized_coords;
488
};
489
490
491
namespace OT {
492
struct cff2_subset_plan
493
{
494
  bool create (const OT::cff2::accelerator_subset_t &acc,
495
        hb_subset_plan_t *plan)
496
0
  {
497
    /* make sure notdef is first */
498
0
    hb_codepoint_t old_glyph;
499
0
    if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
500
501
0
    num_glyphs = plan->num_output_glyphs ();
502
0
    orig_fdcount = acc.fdArray->count;
503
504
0
    drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
505
0
    pinned = (bool) plan->normalized_coords;
506
0
    normalized_coords = plan->normalized_coords;
507
0
    head_maxp_info = plan->head_maxp_info;
508
0
    hmtx_map = &plan->hmtx_map;
509
0
    desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE ||
510
0
         pinned; // For instancing we need this path
511
512
    /* Enable command capture for CFF2→CFF1 conversion (for specialization) */
513
0
    capture_commands = pinned;
514
515
 #ifdef HB_EXPERIMENTAL_API
516
    min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
517
 #else
518
0
    min_charstrings_off_size = 0;
519
0
 #endif
520
521
0
    if (desubroutinize)
522
0
    {
523
      /* Flatten global & local subrs */
524
0
      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
525
0
        flattener(acc, plan);
526
527
      /* Enable command capture if requested (for specialization) */
528
0
      if (capture_commands)
529
0
      {
530
0
  if (!charstring_commands.resize_exact (num_glyphs))
531
0
    return false;
532
533
0
  if (!flattener.flatten (subset_charstrings, &charstring_commands))
534
0
    return false;
535
0
      }
536
0
      else
537
0
      {
538
0
  if (!flattener.flatten (subset_charstrings))
539
0
    return false;
540
0
      }
541
0
    }
542
0
    else
543
0
    {
544
0
      cff2_subr_subsetter_t subr_subsetter (acc, plan);
545
546
      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
547
0
      if (!subr_subsetter.subset ())
548
0
  return false;
549
550
      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
551
0
      if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned))
552
0
  return false;
553
554
0
      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
555
0
  return false;
556
557
      /* local subrs */
558
0
      if (!subset_localsubrs.resize (orig_fdcount))
559
0
  return false;
560
0
      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
561
0
      {
562
0
  subset_localsubrs[fd].init ();
563
0
  if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
564
0
    return false;
565
0
      }
566
0
    }
567
568
    /* FDSelect */
569
0
    if (acc.fdSelect != &Null (CFF2FDSelect))
570
0
    {
571
0
      if (unlikely (!hb_plan_subset_cff_fdselect (plan,
572
0
              orig_fdcount,
573
0
              *(const FDSelect *)acc.fdSelect,
574
0
              subset_fdcount,
575
0
              subset_fdselect_size,
576
0
              subset_fdselect_format,
577
0
              subset_fdselect_ranges,
578
0
              fdmap)))
579
0
  return false;
580
0
    }
581
0
    else
582
0
      fdmap.identity (1);
583
584
0
    return true;
585
0
  }
586
587
  cff2_sub_table_info_t info;
588
589
  unsigned int    num_glyphs;
590
  unsigned int    orig_fdcount = 0;
591
  unsigned int    subset_fdcount = 1;
592
  unsigned int    subset_fdselect_size = 0;
593
  unsigned int    subset_fdselect_format = 0;
594
  bool            pinned = false;
595
  hb_vector_t<code_pair_t>   subset_fdselect_ranges;
596
597
  hb_inc_bimap_t   fdmap;
598
599
  str_buff_vec_t      subset_charstrings;
600
  str_buff_vec_t      subset_globalsubrs;
601
  hb_vector_t<str_buff_vec_t> subset_localsubrs;
602
603
  bool      drop_hints = false;
604
  bool      desubroutinize = false;
605
606
  unsigned  min_charstrings_off_size = 0;
607
608
  hb_array_t<int> normalized_coords; // For instantiation
609
  head_maxp_info_t head_maxp_info;  // For FontBBox
610
  const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>> *hmtx_map; // For widths
611
612
  // Width optimization results (for CFF1 conversion)
613
  unsigned default_width = 0;
614
  unsigned nominal_width = 0;
615
616
  // Command capture for specialization (CFF2→CFF1 conversion)
617
  bool capture_commands = false;
618
  hb_vector_t<hb_vector_t<cs_command_t>> charstring_commands;
619
};
620
} // namespace OT
621
622
/*
623
 * CFF2 to CFF1 Converter Implementation
624
 */
625
626
#include "hb-cff-width-optimizer.hh"
627
#include "hb-cff-specializer.hh"
628
629
/* Serialize charstrings using CFF1 format with widths */
630
static bool
631
_serialize_cff1_charstrings (hb_serialize_context_t *c,
632
                             OT::cff2_subset_plan &plan,
633
                             unsigned default_width,
634
                             unsigned nominal_width)
635
0
{
636
0
  c->push ();
637
638
  // CFF1 requires:
639
  // 1. Width at the beginning (if != defaultWidthX)
640
  // 2. endchar at the end
641
0
  str_buff_vec_t cff1_charstrings;
642
0
  if (unlikely (!cff1_charstrings.resize (plan.subset_charstrings.length)))
643
0
  {
644
0
    c->pop_discard ();
645
0
    return false;
646
0
  }
647
648
0
  for (unsigned i = 0; i < plan.subset_charstrings.length; i++)
649
0
  {
650
    // Get width for this glyph from hmtx_map
651
0
    unsigned width = 0;
652
0
    if (plan.hmtx_map->has (i))
653
0
      width = plan.hmtx_map->get (i).first;
654
655
    // Encode width if different from default
656
0
    str_encoder_t encoder (cff1_charstrings[i]);
657
0
    if (width != default_width)
658
0
    {
659
0
      int delta = (int) width - (int) nominal_width;
660
0
      encoder.encode_int (delta);
661
0
    }
662
663
    // Use specialized commands if available, otherwise use binary
664
0
    if (plan.capture_commands && i < plan.charstring_commands.length &&
665
0
        plan.charstring_commands[i].length > 0)
666
0
    {
667
      // Specialize and encode commands
668
0
      auto &commands = plan.charstring_commands[i];
669
0
      CFF::specialize_commands (commands, 48);  /* maxstack=48 for CFF1 */
670
0
      if (unlikely (!CFF::encode_commands (commands, cff1_charstrings[i])))
671
0
      {
672
0
        c->pop_discard ();
673
0
        return false;
674
0
      }
675
0
    }
676
0
    else
677
0
    {
678
      // Use binary CharString
679
0
      const str_buff_t &cs = plan.subset_charstrings[i];
680
0
      for (unsigned j = 0; j < cs.length; j++)
681
0
        cff1_charstrings[i].push (cs[j]);
682
0
    }
683
684
    // Check if it already ends with endchar (0x0e) or return (0x0b)
685
0
    if (cff1_charstrings[i].length == 0 ||
686
0
        (cff1_charstrings[i].tail () != 0x0e && cff1_charstrings[i].tail () != 0x0b))
687
0
    {
688
      // Append endchar operator
689
0
      if (unlikely (!cff1_charstrings[i].push_or_fail (0x0e)))
690
0
      {
691
0
        c->pop_discard ();
692
0
        return false;
693
0
      }
694
0
    }
695
0
  }
696
697
0
  unsigned data_size = 0;
698
0
  unsigned total_size = CFF1CharStrings::total_size (cff1_charstrings, &data_size);
699
0
  if (unlikely (!c->start_zerocopy (total_size)))
700
0
  {
701
0
    c->pop_discard ();
702
0
    return false;
703
0
  }
704
705
0
  auto *cs = c->start_embed<CFF1CharStrings> ();
706
0
  if (unlikely (!cs->serialize (c, cff1_charstrings)))
707
0
  {
708
0
    c->pop_discard ();
709
0
    return false;
710
0
  }
711
712
0
  plan.info.char_strings_link = c->pop_pack (false);
713
0
  return true;
714
0
}
715
716
/* Serialize CID Charset (format 2 range: gid 0-N -> cid 0-N) */
717
static bool
718
_serialize_cff1_charset (hb_serialize_context_t *c,
719
                         unsigned int num_glyphs,
720
                         objidx_t &charset_link)
721
0
{
722
  // For CID fonts, create a simple identity charset
723
  // Format 2: one range covering all glyphs (except .notdef)
724
0
  c->push ();
725
726
0
  auto *charset = c->start_embed<Charset> ();
727
0
  if (unlikely (!charset))
728
0
  {
729
0
    c->pop_discard ();
730
0
    return false;
731
0
  }
732
733
  // Create a single range for CID 1 to num_glyphs-1
734
0
  hb_vector_t<code_pair_t> ranges;
735
0
  if (num_glyphs > 1)
736
0
  {
737
0
    code_pair_t range;
738
0
    range.code = 1;  // first CID
739
0
    range.glyph = num_glyphs - 2;  // nLeft (covers glyphs 1 to num_glyphs-1)
740
0
    ranges.push (range);
741
0
  }
742
743
0
  if (unlikely (!charset->serialize (c, 2, num_glyphs, ranges)))
744
0
  {
745
0
    c->pop_discard ();
746
0
    return false;
747
0
  }
748
749
0
  charset_link = c->pop_pack ();
750
0
  return true;
751
0
}
752
753
/* CFF2 to CFF1 serialization */
754
namespace CFF {
755
756
bool
757
serialize_cff2_to_cff1 (hb_serialize_context_t *c,
758
                        OT::cff2_subset_plan &plan,
759
                        const cff2_top_dict_values_t &cff2_topDict,
760
                        const OT::cff2::accelerator_subset_t &acc)
761
0
{
762
0
  TRACE_SERIALIZE (this);
763
764
  /*
765
   * CFF1 Serialization Order (reverse, as HarfBuzz packs from end):
766
   * 1. CharStrings
767
   * 2. Private DICs & Local Subrs
768
   * 3. FDArray
769
   * 4. FDSelect
770
   * 5. Charset
771
   * 6. Global Subrs
772
   * 7. String INDEX
773
   * 8. Top DICT INDEX
774
   * 9. Name INDEX
775
   * 10. Header
776
   */
777
778
  // 0. Optimize width encoding (for all FDs)
779
0
  {
780
    // Collect widths from hmtx_map
781
0
    hb_vector_t<unsigned> widths;
782
0
    widths.alloc (plan.num_glyphs);
783
784
0
    for (unsigned gid = 0; gid < plan.num_glyphs; gid++)
785
0
    {
786
0
      unsigned width = 0;
787
0
      if (plan.hmtx_map->has (gid))
788
0
        width = plan.hmtx_map->get (gid).first;
789
0
      widths.push (width);
790
0
    }
791
792
    // Optimize defaultWidthX and nominalWidthX
793
0
    CFF::optimize_widths (widths, plan.default_width, plan.nominal_width);
794
0
  }
795
796
  // 1. CharStrings (with widths prepended)
797
0
  if (!_serialize_cff1_charstrings (c, plan, plan.default_width, plan.nominal_width))
798
0
    return_trace (false);
799
800
  // 2. Private DICs & Local Subrs (same as CFF2)
801
0
  hb_vector_t<table_info_t> private_dict_infos;
802
0
  if (unlikely (!private_dict_infos.resize (plan.subset_fdcount)))
803
0
    return_trace (false);
804
805
0
  for (int i = (int)acc.privateDicts.length; --i >= 0;)
806
0
  {
807
0
    if (plan.fdmap.has (i))
808
0
    {
809
0
      objidx_t subrs_link = 0;
810
811
0
      if (plan.subset_localsubrs[i].length > 0)
812
0
      {
813
0
        auto *dest = c->push<CFF1Subrs> ();
814
0
        if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
815
0
          subrs_link = c->pop_pack (false);
816
0
        else
817
0
        {
818
0
          c->pop_discard ();
819
0
          return_trace (false);
820
0
        }
821
0
      }
822
823
0
      auto *pd = c->push<PrivateDict> ();
824
      // Use the CFF2 Private DICT serializer which instantiates blends when pinned=true
825
0
      cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
826
0
                                                  acc.varStore, plan.normalized_coords);
827
0
      if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
828
0
      {
829
        // Add defaultWidthX and nominalWidthX for CFF1
830
0
        str_buff_t width_ops;
831
0
        str_encoder_t encoder (width_ops);
832
0
        encoder.encode_int (plan.default_width);
833
0
        encoder.encode_op (OpCode_defaultWidthX);
834
0
        encoder.encode_int (plan.nominal_width);
835
0
        encoder.encode_op (OpCode_nominalWidthX);
836
837
0
        if (!encoder.in_error () && c->embed (width_ops.as_bytes ().arrayZ, width_ops.length))
838
0
        {
839
0
          unsigned fd = plan.fdmap[i];
840
0
          private_dict_infos[fd].size = c->length ();
841
0
          private_dict_infos[fd].link = c->pop_pack ();
842
0
        }
843
0
        else
844
0
        {
845
0
          c->pop_discard ();
846
0
          return_trace (false);
847
0
        }
848
0
      }
849
0
      else
850
0
      {
851
0
        c->pop_discard ();
852
0
        return_trace (false);
853
0
      }
854
0
    }
855
0
  }
856
857
  // 3. FDArray - serialize CFF2 font dicts as CFF1
858
0
  {
859
0
    auto *fda = c->push<FDArray<HBUINT16>> ();
860
0
    cff_font_dict_op_serializer_t fontSzr;
861
0
    auto it =
862
0
    + hb_zip (+ hb_iter (acc.fontDicts)
863
0
              | hb_filter ([&] (const cff2_font_dict_values_t &_)
864
0
                { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
865
0
              hb_iter (private_dict_infos))
866
0
    ;
867
    // Explicitly specify template parameters: DICTVAL, INFO
868
0
    bool success = fda->serialize<cff2_font_dict_values_t, table_info_t> (c, it, fontSzr);
869
0
    if (success)
870
0
      plan.info.fd_array_link = c->pop_pack (false);
871
0
    else
872
0
    {
873
0
      c->pop_discard ();
874
0
      return_trace (false);
875
0
    }
876
0
  }
877
878
  // 4. FDSelect (required in CFF1 CID-keyed fonts)
879
  // CFF1 requires FDSelect for all CID-keyed fonts, even with just one FD
880
  // CFF2 makes it optional when there's only one FD
881
0
  if (acc.fdSelect != &Null (CFF2FDSelect))
882
0
  {
883
0
    c->push ();
884
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs,
885
0
                                          *(const FDSelect *)acc.fdSelect,
886
0
                                          plan.orig_fdcount,
887
0
                                          plan.subset_fdselect_format,
888
0
                                          plan.subset_fdselect_size,
889
0
                                          plan.subset_fdselect_ranges)))
890
0
      plan.info.fd_select.link = c->pop_pack ();
891
0
    else
892
0
    {
893
0
      c->pop_discard ();
894
0
      return_trace (false);
895
0
    }
896
0
  }
897
0
  else
898
0
  {
899
    // Create a range-based FDSelect3 mapping all glyphs to FD 0
900
    // Format: format(1) + nRanges(2) + range(3) + sentinel(2) = 8 bytes
901
0
    c->push ();
902
903
    // Format byte
904
0
    HBUINT8 format;
905
0
    format = 3;
906
0
    if (unlikely (!c->embed (format)))
907
0
    {
908
0
      c->pop_discard ();
909
0
      return_trace (false);
910
0
    }
911
912
    // nRanges
913
0
    HBUINT16 nRanges;
914
0
    nRanges = 1;
915
0
    if (unlikely (!c->embed (nRanges)))
916
0
    {
917
0
      c->pop_discard ();
918
0
      return_trace (false);
919
0
    }
920
921
    // Single range: {first: 0, fd: 0}
922
0
    FDSelect3_Range range;
923
0
    range.first = 0;
924
0
    range.fd = 0;
925
0
    if (unlikely (!c->embed (range)))
926
0
    {
927
0
      c->pop_discard ();
928
0
      return_trace (false);
929
0
    }
930
931
    // Sentinel (number of glyphs)
932
0
    HBUINT16 sentinel;
933
0
    sentinel = plan.num_glyphs;
934
0
    if (unlikely (!c->embed (sentinel)))
935
0
    {
936
0
      c->pop_discard ();
937
0
      return_trace (false);
938
0
    }
939
940
0
    plan.info.fd_select.link = c->pop_pack ();
941
0
  }
942
943
  // 5. Charset (CID charset for identity mapping)
944
0
  objidx_t charset_link;
945
0
  if (!_serialize_cff1_charset (c, plan.num_glyphs, charset_link))
946
0
    return_trace (false);
947
948
  // 6. 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_trace (false);
957
0
    }
958
0
  }
959
960
  // 7. String INDEX - Add "Adobe" and "Identity" for ROS operator
961
0
  {
962
0
    const char *adobe_str = "Adobe";
963
0
    const char *identity_str = "Identity";
964
0
    unsigned adobe_len = 5;  // strlen("Adobe")
965
0
    unsigned identity_len = 8;  // strlen("Identity")
966
967
    // Build strings array
968
0
    hb_vector_t<hb_ubytes_t> strings;
969
0
    strings.alloc (2);
970
0
    strings.push (hb_ubytes_t ((const unsigned char *) adobe_str, adobe_len));
971
0
    strings.push (hb_ubytes_t ((const unsigned char *) identity_str, identity_len));
972
973
    // Serialize as CFF INDEX
974
0
    auto *dest = c->push<CFF1Index> ();
975
0
    if (likely (dest->serialize (c, strings)))
976
0
      c->pop_pack (false);
977
0
    else
978
0
    {
979
0
      c->pop_discard ();
980
0
      return_trace (false);
981
0
    }
982
0
  }
983
984
  // 8. CFF Header
985
0
  OT::cff1 *cff = c->allocate_min<OT::cff1> ();
986
0
  if (unlikely (!cff)) return_trace (false);
987
988
  /* header */
989
0
  cff->version.major = 0x01;
990
0
  cff->version.minor = 0x00;
991
0
  cff->nameIndex = cff->min_size;
992
0
  cff->offSize = 4; /* unused? */
993
994
  // 9. Name INDEX (single entry)
995
0
  {
996
0
    unsigned name_len = strlen (CFF1_DEFAULT_FONT_NAME);
997
998
0
    CFF1Index *idx = c->start_embed<CFF1Index> ();
999
0
    if (unlikely (!idx)) return_trace (false);
1000
1001
0
    if (unlikely (!idx->serialize_header (c, hb_iter (&name_len, 1), name_len)))
1002
0
      return_trace (false);
1003
1004
0
    if (unlikely (!c->embed (CFF1_DEFAULT_FONT_NAME, name_len)))
1005
0
      return_trace (false);
1006
0
  }
1007
1008
  // 10. Top DICT INDEX
1009
0
  {
1010
    // Serialize the Top DICT data first
1011
0
    c->push<TopDict> ();
1012
0
    cff1_from_cff2_top_dict_op_serializer_t topSzr;
1013
1014
    // Serialize ROS first
1015
0
    if (unlikely (!topSzr.serialize_ros (c)))
1016
0
    {
1017
0
      c->pop_discard ();
1018
0
      return_trace (false);
1019
0
    }
1020
1021
    // Serialize FontBBox from head table
1022
0
    {
1023
0
      str_buff_t bbox_buff;
1024
0
      str_encoder_t encoder (bbox_buff);
1025
1026
0
      encoder.encode_int (plan.head_maxp_info.xMin);
1027
0
      encoder.encode_int (plan.head_maxp_info.yMin);
1028
0
      encoder.encode_int (plan.head_maxp_info.xMax);
1029
0
      encoder.encode_int (plan.head_maxp_info.yMax);
1030
0
      encoder.encode_op (OpCode_FontBBox);
1031
1032
0
      if (encoder.in_error () || !c->embed (bbox_buff.as_bytes ().arrayZ, bbox_buff.length))
1033
0
      {
1034
0
        c->pop_discard ();
1035
0
        return_trace (false);
1036
0
      }
1037
0
    }
1038
1039
    // Serialize charset operator
1040
0
    if (charset_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_charset, charset_link, whence_t::Absolute)))
1041
0
    {
1042
0
      c->pop_discard ();
1043
0
      return_trace (false);
1044
0
    }
1045
1046
    // Serialize FDSelect operator (required for CID-keyed CFF1 fonts)
1047
0
    if (plan.info.fd_select.link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDSelect, plan.info.fd_select.link, whence_t::Absolute)))
1048
0
    {
1049
0
      c->pop_discard ();
1050
0
      return_trace (false);
1051
0
    }
1052
1053
    // Serialize FDArray operator (required for CID-keyed CFF1 fonts)
1054
0
    if (plan.info.fd_array_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDArray, plan.info.fd_array_link, whence_t::Absolute)))
1055
0
    {
1056
0
      c->pop_discard ();
1057
0
      return_trace (false);
1058
0
    }
1059
1060
    // Serialize other operators from CFF2 TopDict
1061
0
    for (const auto &opstr : cff2_topDict.values)
1062
0
    {
1063
0
      if (unlikely (!topSzr.serialize (c, opstr, plan.info)))
1064
0
      {
1065
0
        c->pop_discard ();
1066
0
        return_trace (false);
1067
0
      }
1068
0
    }
1069
1070
0
    unsigned top_size = c->length ();
1071
0
    c->pop_pack (false);
1072
1073
    // Serialize INDEX header
1074
0
    auto *dest = c->start_embed<CFF1Index> ();
1075
0
    if (unlikely (!dest->serialize_header (c, hb_iter (&top_size, 1), top_size)))
1076
0
      return_trace (false);
1077
0
  }
1078
1079
0
  return_trace (true);
1080
0
}
1081
1082
} /* namespace CFF */
1083
1084
static bool _serialize_cff2_charstrings (hb_serialize_context_t *c,
1085
           cff2_subset_plan &plan,
1086
           const OT::cff2::accelerator_subset_t  &acc)
1087
0
{
1088
0
  c->push ();
1089
1090
0
  unsigned data_size = 0;
1091
0
  unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
1092
0
  if (unlikely (!c->start_zerocopy (total_size)))
1093
0
    return false;
1094
1095
0
  auto *cs = c->start_embed<CFF2CharStrings> ();
1096
0
  if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size)))
1097
0
  {
1098
0
    c->pop_discard ();
1099
0
    return false;
1100
0
  }
1101
1102
0
  plan.info.char_strings_link = c->pop_pack (false);
1103
0
  return true;
1104
0
}
1105
1106
bool
1107
OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
1108
             struct cff2_subset_plan &plan,
1109
             hb_array_t<int> normalized_coords) const
1110
0
{
1111
  /* push charstrings onto the object stack first which will ensure it packs as the last
1112
     object in the table. Keeping the chastrings last satisfies the requirements for patching
1113
     via IFTB. If this ordering needs to be changed in the future, charstrings should be left
1114
     at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
1115
0
  if (!_serialize_cff2_charstrings(c, plan, *this))
1116
0
    return false;
1117
1118
  /* private dicts & local subrs */
1119
0
  hb_vector_t<table_info_t>  private_dict_infos;
1120
0
  if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
1121
1122
0
  for (int i = (int)privateDicts.length; --i >= 0 ;)
1123
0
  {
1124
0
    if (plan.fdmap.has (i))
1125
0
    {
1126
0
      objidx_t  subrs_link = 0;
1127
1128
0
      if (plan.subset_localsubrs[i].length > 0)
1129
0
      {
1130
0
  auto *dest = c->push <CFF2Subrs> ();
1131
0
  if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
1132
0
    subrs_link = c->pop_pack (false);
1133
0
  else
1134
0
  {
1135
0
    c->pop_discard ();
1136
0
    return false;
1137
0
  }
1138
0
      }
1139
0
      auto *pd = c->push<PrivateDict> ();
1140
0
      cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
1141
0
             varStore, normalized_coords);
1142
0
      if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
1143
0
      {
1144
0
  unsigned fd = plan.fdmap[i];
1145
0
  private_dict_infos[fd].size = c->length ();
1146
0
  private_dict_infos[fd].link = c->pop_pack ();
1147
0
      }
1148
0
      else
1149
0
      {
1150
0
  c->pop_discard ();
1151
0
  return false;
1152
0
      }
1153
0
    }
1154
0
  }
1155
1156
  /* FDSelect */
1157
0
  if (fdSelect != &Null (CFF2FDSelect))
1158
0
  {
1159
0
    c->push ();
1160
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
1161
0
             plan.orig_fdcount,
1162
0
             plan.subset_fdselect_format, plan.subset_fdselect_size,
1163
0
             plan.subset_fdselect_ranges)))
1164
0
      plan.info.fd_select.link = c->pop_pack ();
1165
0
    else
1166
0
    {
1167
0
      c->pop_discard ();
1168
0
      return false;
1169
0
    }
1170
0
  }
1171
1172
  /* FDArray (FD Index) */
1173
0
  {
1174
0
    auto *fda = c->push<CFF2FDArray> ();
1175
0
    cff_font_dict_op_serializer_t fontSzr;
1176
0
    auto it =
1177
0
    + hb_zip (+ hb_iter (fontDicts)
1178
0
        | hb_filter ([&] (const cff2_font_dict_values_t &_)
1179
0
    { return plan.fdmap.has (&_ - &fontDicts[0]); }),
1180
0
        hb_iter (private_dict_infos))
1181
0
    ;
1182
0
    if (unlikely (!fda->serialize (c, it, fontSzr)))
1183
0
    {
1184
0
      c->pop_discard ();
1185
0
      return false;
1186
0
    }
1187
0
    plan.info.fd_array_link = c->pop_pack (false);
1188
0
  }
1189
1190
  /* variation store */
1191
0
  if (varStore != &Null (CFF2ItemVariationStore) &&
1192
0
      !plan.pinned)
1193
0
  {
1194
0
    auto *dest = c->push<CFF2ItemVariationStore> ();
1195
0
    if (unlikely (!dest->serialize (c, varStore)))
1196
0
    {
1197
0
      c->pop_discard ();
1198
0
      return false;
1199
0
    }
1200
0
    plan.info.var_store_link = c->pop_pack (false);
1201
0
  }
1202
1203
0
  OT::cff2 *cff2 = c->allocate_min<OT::cff2> ();
1204
0
  if (unlikely (!cff2)) return false;
1205
1206
  /* header */
1207
0
  cff2->version.major = 0x02;
1208
0
  cff2->version.minor = 0x00;
1209
0
  cff2->topDict = OT::cff2::static_size;
1210
1211
  /* top dict */
1212
0
  {
1213
0
    TopDict &dict = cff2 + cff2->topDict;
1214
0
    cff2_top_dict_op_serializer_t topSzr;
1215
0
    if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false;
1216
0
    cff2->topDictSize = c->head - (const char *)&dict;
1217
0
  }
1218
1219
  /* global subrs */
1220
0
  {
1221
0
    auto *dest = c->start_embed <CFF2Subrs> ();
1222
0
    return dest->serialize (c, plan.subset_globalsubrs);
1223
0
  }
1224
0
}
1225
1226
bool
1227
OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const
1228
0
{
1229
0
  cff2_subset_plan cff2_plan;
1230
1231
0
  if (unlikely (!cff2_plan.create (*this, c->plan))) return false;
1232
1233
  // If instantiating (pinned) and downgrade flag is set, convert to CFF1
1234
0
  if (cff2_plan.pinned && (c->plan->flags & HB_SUBSET_FLAGS_DOWNGRADE_CFF2))
1235
0
  {
1236
    // Serialize CFF1 to the subsetter's serializer
1237
    // If we run out of room, returning true will cause subsetter to retry with larger buffer
1238
0
    bool result = CFF::serialize_cff2_to_cff1 (c->serializer, cff2_plan, topDict, *this);
1239
1240
0
    if (c->serializer->ran_out_of_room ())
1241
0
      return true; // Subsetter will retry with larger buffer
1242
1243
0
    if (result && !c->serializer->in_error ())
1244
0
    {
1245
      // Success - end serialization to resolve links
1246
0
      c->serializer->end_serialize ();
1247
1248
      // Copy the serialized CFF1 data and add as CFF table
1249
0
      hb_blob_t *cff_blob = c->serializer->copy_blob ();
1250
0
      if (cff_blob)
1251
0
      {
1252
0
        c->plan->add_table (HB_TAG('C','F','F',' '), cff_blob);
1253
0
        hb_blob_destroy (cff_blob);
1254
1255
        // Return false to signal CFF2 table is not needed
1256
0
        return false;
1257
0
      }
1258
0
    }
1259
1260
    // Conversion failed - don't fall back, fail hard for debugging
1261
0
    return false;
1262
0
  }
1263
1264
0
  return serialize (c->serializer, cff2_plan,
1265
0
        c->plan->normalized_coords.as_array ());
1266
0
}
1267
1268
#endif