Coverage Report

Created: 2026-09-28 10:59

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-ot-var-common.hh"
34
#include "hb-set.h"
35
#include "hb-subset-plan.hh"
36
#include "hb-subset-cff-common.hh"
37
#include "hb-cff2-interp-cs.hh"
38
#include "hb-subset-cff2-to-cff1.hh"
39
40
using namespace CFF;
41
42
namespace CFF {
43
44
/*
45
 * Partial instancing (some axes pinned or limited, others kept).
46
 *
47
 * This is a port of fontTools' instantiateCFF2 (fonttools/fonttools#3506),
48
 * with one simplification: the variation-store instancing solver acts on
49
 * regions independently of the per-blend delta values, so instead of lifting
50
 * every blend's deltas into the store, instancing it, and reading the rows
51
 * back, we instance each VarData once with identity delta rows.  The
52
 * instantiated tuples then give, per VarData, a fixed linear map from old
53
 * per-region deltas to new per-region deltas.  The contribution that folds
54
 * into each blend's default value is the old store's region scalars
55
 * evaluated at the new default location (plan->normalized_coords); the
56
 * surviving rebased regions all evaluate to zero there.
57
 *
58
 * Use: create (), then note_use () for every blend the retained charstrings
59
 * and private dicts hold, then finalize (); only VarDatas that are both
60
 * still variable and actually used survive into the new store.
61
 */
62
struct cff2_instancing_plan_t
63
{
64
  struct vardata_transform_t
65
  {
66
    /* new_deltas[j] = Σ_i matrix[j][i] * old_deltas[i] */
67
    hb_vector_t<hb_vector_t<float>> matrix;
68
    /* Default-value fold-in scalars, one per old region: the old store's
69
     * region scalars at the new default location. */
70
    hb_vector_t<float> gains;
71
    /* Surviving regions, parallel to matrix rows. */
72
    hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>> regions;
73
    /* Index into the new global region list, per new region (finalize). */
74
    hb_vector_t<unsigned> region_indices;
75
    unsigned new_major = 0;
76
    bool alive_ = false;
77
0
    bool alive () const { return alive_; }
78
79
    /* One value's delta for new region j, from its old per-region deltas. */
80
    double transform_delta (unsigned j, hb_array_t<const number_t> deltas) const
81
0
    {
82
0
      const hb_vector_t<float> &col = matrix.arrayZ[j];
83
0
      double d = 0.;
84
0
      unsigned count = hb_min (col.length, deltas.length);
85
0
      for (unsigned c = 0; c < count; c++)
86
0
  d += (double) col.arrayZ[c] * deltas.arrayZ[c].to_real ();
87
0
      return d;
88
0
    }
89
  };
90
91
  hb_vector_t<vardata_transform_t> transforms;  /* per old VarData (vsindex) */
92
  hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>> new_regions;  /* global region list */
93
  hb_set_t used_majors;
94
  unsigned new_major_count = 0;
95
96
0
  bool has_variations () const { return new_major_count; }
97
98
  const vardata_transform_t *get_transform (unsigned major) const
99
0
  { return major < transforms.length ? &transforms.arrayZ[major] : nullptr; }
100
101
  /* Record that a retained blend references this VarData. */
102
0
  void note_use (unsigned major) { used_majors.add (major); }
103
104
  /* The new vsindex a private dict's (or charstring's) old ivs maps to;
105
   * dead VarDatas fall back to 0, their blends having become static. */
106
  unsigned remap_ivs (unsigned old_ivs) const
107
0
  {
108
0
    const vardata_transform_t *t = get_transform (old_ivs);
109
0
    return (t && t->alive ()) ? t->new_major : 0;
110
0
  }
111
112
  double fold_deltas (unsigned old_ivs, hb_array_t<const number_t> deltas) const
113
0
  {
114
0
    const vardata_transform_t *t = get_transform (old_ivs);
115
0
    if (unlikely (!t || t->gains.length != deltas.length)) return 0.;
116
0
    double v = 0.;
117
0
    for (unsigned i = 0; i < deltas.length; i++)
118
0
      v += (double) t->gains.arrayZ[i] * deltas.arrayZ[i].to_real ();
119
0
    return v;
120
0
  }
121
122
  bool create (const CFF2ItemVariationStore *cff2VarStore,
123
         const hb_subset_plan_t *plan)
124
0
  {
125
0
    const OT::ItemVariationStore &varStore = cff2VarStore->varStore;
126
0
    unsigned major_count = varStore.get_sub_table_count ();
127
0
    if (!transforms.resize (major_count)) return false;
128
129
0
    if (!varStore.get_region_list ().get_var_regions (plan->axes_old_index_tag_map, orig_regions))
130
0
      return false;
131
132
    /* Instance each VarData with identity delta rows; the surviving tuples'
133
     * delta columns are the old-region → new-region transform. */
134
0
    OT::optimize_scratch_t scratch;
135
0
    for (unsigned m = 0; m < major_count; m++)
136
0
    {
137
0
      const OT::VarData &var_data = varStore.get_sub_table (m);
138
0
      unsigned k = var_data.get_region_index_count ();
139
0
      vardata_transform_t &t = transforms.arrayZ[m];
140
141
0
      if (!t.gains.resize_exact (k)) return false;
142
0
      varStore.get_region_scalars (m, plan->normalized_coords.arrayZ,
143
0
           plan->normalized_coords.length,
144
0
           t.gains.arrayZ, k);
145
146
0
      OT::tuple_variations_t tuples;
147
0
      for (unsigned r = 0; r < k; r++)
148
0
      {
149
0
  OT::tuple_delta_t tuple;
150
0
  if (!tuple.deltas_x.resize (k) ||
151
0
      !tuple.indices.resize (k))
152
0
    return false;
153
0
  for (unsigned i = 0; i < k; i++)
154
0
    tuple.indices.arrayZ[i] = true;
155
0
  tuple.deltas_x.arrayZ[r] = 1.f;
156
0
  unsigned region_index = var_data.get_region_index (r);
157
0
  if (unlikely (region_index >= orig_regions.length)) return false;
158
0
  tuple.axis_tuples = orig_regions.arrayZ[region_index];
159
0
  if (unlikely (tuple.axis_tuples.in_error ())) return false;
160
0
  tuples.tuple_vars.push (std::move (tuple));
161
0
      }
162
0
      if (unlikely (tuples.tuple_vars.in_error ())) return false;
163
164
0
      if (!tuples.instantiate (plan->axes_location, plan->axes_triple_distances, scratch))
165
0
  return false;
166
167
0
      for (auto &tuple : tuples.tuple_vars)
168
0
      {
169
0
  if (unlikely (tuple.deltas_x.length != k)) return false;
170
0
  hb_vector_t<float> col;
171
0
  if (unlikely (!col.resize_exact (k))) return false;
172
0
  for (unsigned i = 0; i < k; i++)
173
0
    col.arrayZ[i] = tuple.deltas_x.arrayZ[i];
174
0
  t.matrix.push (std::move (col));
175
0
  t.regions.push (std::move (tuple.axis_tuples));
176
0
      }
177
0
      if (unlikely (t.matrix.in_error () || t.regions.in_error ())) return false;
178
0
    }
179
180
0
    return true;
181
0
  }
182
183
  /* Decide which VarDatas survive, number them, and build the new global
184
   * region list: surviving original regions first, in their original order,
185
   * then solver-split new regions in order of appearance (fontTools'
186
   * rebuildRegions + prune_regions). */
187
  bool finalize ()
188
0
  {
189
0
    for (unsigned m = 0; m < transforms.length; m++)
190
0
    {
191
0
      vardata_transform_t &t = transforms.arrayZ[m];
192
0
      t.alive_ = t.matrix.length && used_majors.has (m);
193
0
      if (t.alive_)
194
0
  t.new_major = new_major_count++;
195
0
    }
196
197
    /* Keys hash by content, so pointers into different owners compare fine. */
198
0
    hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> region_idx_map;
199
0
    hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple>*> region_ptrs;
200
0
    for (const vardata_transform_t &t : transforms)
201
0
    {
202
0
      if (!t.alive ()) continue;
203
0
      for (const auto &region : t.regions)
204
0
  if (!region_idx_map.has (&region))
205
0
    if (unlikely (!region_idx_map.set (&region, (unsigned) -1)))
206
0
      return false;
207
0
    }
208
0
    for (const auto &r : orig_regions)
209
0
    {
210
0
      unsigned *idx;
211
0
      if (region_idx_map.has (&r, &idx) && *idx == (unsigned) -1)
212
0
      {
213
0
  if (unlikely (!region_idx_map.set (&r, region_ptrs.length))) return false;
214
0
  region_ptrs.push (&r);
215
0
      }
216
0
    }
217
0
    for (const vardata_transform_t &t : transforms)
218
0
    {
219
0
      if (!t.alive ()) continue;
220
0
      for (const auto &region : t.regions)
221
0
      {
222
0
  unsigned *idx;
223
0
  if (region_idx_map.has (&region, &idx) && *idx == (unsigned) -1)
224
0
  {
225
0
    if (unlikely (!region_idx_map.set (&region, region_ptrs.length))) return false;
226
0
    region_ptrs.push (&region);
227
0
  }
228
0
      }
229
0
    }
230
0
    if (unlikely (region_ptrs.in_error ())) return false;
231
232
0
    for (vardata_transform_t &t : transforms)
233
0
    {
234
0
      if (!t.alive ()) continue;
235
0
      for (const auto &region : t.regions)
236
0
      {
237
0
  unsigned *idx;
238
0
  if (unlikely (!region_idx_map.has (&region, &idx))) return false;
239
0
  t.region_indices.push (*idx);
240
0
      }
241
0
      if (unlikely (t.region_indices.in_error ())) return false;
242
0
    }
243
244
0
    if (!new_regions.resize (region_ptrs.length)) return false;
245
0
    for (unsigned i = 0; i < region_ptrs.length; i++)
246
0
    {
247
0
      new_regions.arrayZ[i] = *region_ptrs.arrayZ[i];
248
0
      if (unlikely (new_regions.arrayZ[i].in_error ())) return false;
249
0
    }
250
251
0
    return true;
252
0
  }
253
254
  /* Serialize the instanced store: a CFF2ItemVariationStore whose VarDatas
255
   * carry no items, just region indexes (blend deltas live in charstrings).
256
   * Laid out manually since all sizes are known up front. */
257
  bool serialize_var_store (hb_serialize_context_t *c,
258
          const hb_vector_t<hb_tag_t> &axis_tags) const
259
0
  {
260
0
    unsigned data_count = new_major_count;
261
0
    unsigned header_size = 2 + 4 + 2 + 4 * data_count;
262
263
0
    uint64_t offset = header_size;
264
0
    hb_vector_t<unsigned> data_offsets;
265
0
    for (const vardata_transform_t &t : transforms)
266
0
    {
267
0
      if (!t.alive ()) continue;
268
0
      data_offsets.push ((unsigned) offset);
269
0
      offset += 6 + 2 * (uint64_t) t.region_indices.length;
270
0
    }
271
0
    if (unlikely (data_offsets.in_error () || data_offsets.length != data_count))
272
0
      return false;
273
0
    unsigned regions_offset = (unsigned) offset;
274
0
    uint64_t total = offset + 4 + (uint64_t) new_regions.length * axis_tags.length * 6;
275
0
    if (unlikely (total > 0xFFFFu)) return false;
276
277
0
    HBUINT16 *size_field = c->allocate_size<HBUINT16> (HBUINT16::static_size);
278
0
    if (unlikely (!size_field)) return false;
279
0
    *size_field = total;
280
281
0
    char *header = c->allocate_size<char> (header_size);
282
0
    if (unlikely (!header)) return false;
283
0
    * (HBUINT16 *) (header + 0) = 1;      /* format */
284
0
    * (HBUINT32 *) (header + 2) = regions_offset;
285
0
    * (HBUINT16 *) (header + 6) = data_count;
286
0
    for (unsigned i = 0; i < data_count; i++)
287
0
      * (HBUINT32 *) (header + 8 + 4 * i) = data_offsets.arrayZ[i];
288
289
0
    for (const vardata_transform_t &t : transforms)
290
0
    {
291
0
      if (!t.alive ()) continue;
292
0
      unsigned k = t.region_indices.length;
293
0
      HBUINT16 *v = (HBUINT16 *) c->allocate_size<char> (6 + 2 * k);
294
0
      if (unlikely (!v)) return false;
295
0
      v[0] = 0;           /* itemCount */
296
0
      v[1] = 0;           /* wordSizeCount */
297
0
      v[2] = k;           /* regionIndices.len */
298
0
      for (unsigned j = 0; j < k; j++)
299
0
  v[3 + j] = t.region_indices.arrayZ[j];
300
0
    }
301
302
0
    hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple>*> region_ptrs;
303
0
    if (unlikely (!region_ptrs.alloc_exact (new_regions.length))) return false;
304
0
    for (const auto &r : new_regions)
305
0
      region_ptrs.push (&r);
306
0
    auto *region_list = c->start_embed<OT::VarRegionList> ();
307
0
    return region_list->serialize (c, axis_tags, region_ptrs);
308
0
  }
309
310
  private:
311
  hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>> orig_regions;
312
};
313
314
/* Usage-collection pass for partial instancing: record which VarDatas the
315
 * retained charstrings' and private dicts' blends reference.  Mirrors the
316
 * emission pass: blends consumed by dropped hint ops don't count. */
317
struct cff2_usage_param_t
318
{
319
0
  void init () {}
320
321
  cff2_instancing_plan_t *instancing = nullptr;
322
  bool drop_hints = false;
323
  unsigned ivs = 0; /* dict-side current vsindex */
324
};
325
326
struct cff2_cs_opset_usage_t : cff2_cs_opset_t<cff2_cs_opset_usage_t, cff2_usage_param_t, blend_arg_t>
327
{
328
  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, cff2_usage_param_t& param)
329
0
  {
330
0
    bool counts = true;
331
0
    switch (op)
332
0
    {
333
0
      case OpCode_return:
334
0
      case OpCode_endchar:
335
0
  counts = false;
336
0
  break;
337
338
0
      case OpCode_hstem:
339
0
      case OpCode_hstemhm:
340
0
      case OpCode_vstem:
341
0
      case OpCode_vstemhm:
342
0
      case OpCode_hintmask:
343
0
      case OpCode_cntrmask:
344
0
  if (param.drop_hints)
345
0
    counts = false;
346
0
  break;
347
348
0
      default:
349
0
  break;
350
0
    }
351
352
0
    if (counts)
353
0
      for (unsigned int i = 0; i < env.argStack.get_count (); i++)
354
0
  if (env.argStack[i].blending ())
355
0
  {
356
0
    param.instancing->note_use (env.get_ivs ());
357
0
    break;
358
0
  }
359
360
0
    env.clear_args ();
361
0
  }
362
};
363
364
struct cff2_private_dict_usage_opset_t : dict_opset_t
365
{
366
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_usage_param_t& param)
367
0
  {
368
0
    switch (op) {
369
0
      case OpCode_vsindexdict:
370
0
  env.process_vsindex ();
371
0
  param.ivs = env.get_ivs ();
372
0
  env.clear_args ();
373
0
  return;
374
0
      case OpCode_blenddict:
375
0
  param.instancing->note_use (param.ivs);
376
0
  env.clear_args ();
377
0
  return;
378
0
      default:
379
0
  dict_opset_t::process_op (op, env);
380
0
  if (!env.argStack.is_empty ()) return;
381
0
  env.clear_args ();
382
0
  return;
383
0
    }
384
0
  }
385
};
386
387
} /* namespace CFF */
388
389
struct cff2_sub_table_info_t : cff_sub_table_info_t
390
{
391
  cff2_sub_table_info_t ()
392
0
    : cff_sub_table_info_t (),
393
0
      var_store_link (0)
394
0
  {}
395
396
  objidx_t  var_store_link;
397
};
398
399
struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
400
{
401
  bool serialize (hb_serialize_context_t *c,
402
      const op_str_t &opstr,
403
      const cff2_sub_table_info_t &info) const
404
0
  {
405
0
    TRACE_SERIALIZE (this);
406
407
0
    switch (opstr.op)
408
0
    {
409
0
      case OpCode_vstore:
410
0
        if (info.var_store_link)
411
0
    return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
412
0
  else
413
0
    return_trace (true);
414
415
0
      default:
416
0
  return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info));
417
0
    }
418
0
  }
419
};
420
421
struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
422
{
423
  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
424
0
  {
425
    /* Optionally capture command for specialization (before flushing, to preserve args) */
426
0
    if (param.commands)
427
0
    {
428
0
      bool skip_command = false;
429
430
0
      switch (op)
431
0
      {
432
0
  case OpCode_return:
433
0
  case OpCode_endchar:
434
0
    skip_command = true;
435
0
    break;
436
437
0
  case OpCode_hstem:
438
0
  case OpCode_hstemhm:
439
0
  case OpCode_vstem:
440
0
  case OpCode_vstemhm:
441
0
  case OpCode_hintmask:
442
0
  case OpCode_cntrmask:
443
0
    if (param.drop_hints)
444
0
      skip_command = true;
445
0
    break;
446
447
0
  default:
448
0
    break;
449
0
      }
450
451
0
      if (!skip_command)
452
0
      {
453
0
  cs_command_t cmd (op);
454
  /* Capture resolved blend values */
455
0
  for (unsigned int i = 0; i < env.argStack.get_count ();)
456
0
  {
457
0
    const blend_arg_t &arg = env.argStack[i];
458
0
    if (arg.blending ())
459
0
    {
460
      /* For blend args, capture only the resolved default value */
461
0
      cmd.args.push (arg);
462
      /* Skip over the multiple blend values */
463
0
      i += arg.numValues;
464
0
    }
465
0
    else
466
0
    {
467
0
      cmd.args.push (arg);
468
0
      i++;
469
0
    }
470
0
  }
471
0
  param.commands->push (cmd);
472
0
      }
473
0
    }
474
475
0
    switch (op)
476
0
    {
477
0
      case OpCode_return:
478
0
      case OpCode_endchar:
479
  /* dummy opcodes in CFF2. ignore */
480
0
  if (op == OpCode_endchar && param.instancer)
481
0
    emit_vsindex_prefix (env, param);
482
0
  break;
483
484
0
      case OpCode_hstem:
485
0
      case OpCode_hstemhm:
486
0
      case OpCode_vstem:
487
0
      case OpCode_vstemhm:
488
0
      case OpCode_hintmask:
489
0
      case OpCode_cntrmask:
490
0
  if (param.drop_hints)
491
0
  {
492
0
    env.clear_args ();
493
0
    return;
494
0
  }
495
0
  HB_FALLTHROUGH;
496
497
0
      default:
498
0
  SUPER::flush_args_and_op (op, env, param);
499
0
  break;
500
0
    }
501
0
  }
502
503
  static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
504
0
  {
505
0
    for (unsigned int i = 0; i < env.argStack.get_count ();)
506
0
    {
507
0
      const blend_arg_t &arg = env.argStack[i];
508
0
      if (arg.blending ())
509
0
      {
510
0
  if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
511
0
  {
512
0
    env.set_error ();
513
0
    return;
514
0
  }
515
0
  if (param.instancer)
516
0
    rewrite_blends (arg, i, env, param);
517
0
  else
518
0
    flatten_blends (arg, i, env, param);
519
0
  i += arg.numValues;
520
0
      }
521
0
      else
522
0
      {
523
0
  str_encoder_t  encoder (param.flatStr);
524
0
  encoder.encode_num_cs (arg);
525
0
  i++;
526
0
      }
527
0
    }
528
0
    SUPER::flush_args (env, param);
529
0
  }
530
531
  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)
532
0
  {
533
    /* flatten the default values */
534
0
    str_encoder_t  encoder (param.flatStr);
535
0
    for (unsigned int j = 0; j < arg.numValues; j++)
536
0
    {
537
0
      const blend_arg_t &arg1 = env.argStack[i + j];
538
0
      if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
539
0
        (arg1.deltas.length == env.get_region_count ())))))
540
0
      {
541
0
  env.set_error ();
542
0
  return;
543
0
      }
544
0
      encoder.encode_num_cs (arg1);
545
0
    }
546
    /* flatten deltas for each value */
547
0
    for (unsigned int j = 0; j < arg.numValues; j++)
548
0
    {
549
0
      const blend_arg_t &arg1 = env.argStack[i + j];
550
0
      for (unsigned int k = 0; k < arg1.deltas.length; k++)
551
0
  encoder.encode_num_cs (arg1.deltas[k]);
552
0
    }
553
    /* flatten the number of values followed by blend operator */
554
0
    encoder.encode_int (arg.numValues);
555
0
    encoder.encode_op (OpCode_blendcs);
556
0
  }
557
558
  /* Partial instancing: rewrite one blend group against the instanced store.
559
   * Each value's default absorbs the folded (pinned-away) contribution, and
560
   * its deltas are mapped onto the new region list. */
561
  static void rewrite_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
562
0
  {
563
0
    const cff2_instancing_plan_t &instancer = *param.instancer;
564
0
    unsigned ivs = env.get_ivs ();
565
0
    const cff2_instancing_plan_t::vardata_transform_t *t = instancer.get_transform (ivs);
566
0
    if (unlikely (!t))
567
0
    {
568
0
      env.set_error ();
569
0
      return;
570
0
    }
571
572
0
    unsigned n = arg.numValues;
573
0
    for (unsigned int j = 0; j < n; j++)
574
0
    {
575
0
      const blend_arg_t &arg1 = env.argStack[i + j];
576
0
      if (unlikely (!(arg1.blending () && (arg1.numValues == n) && (arg1.valueIndex == j) &&
577
0
          (arg1.deltas.length == env.get_region_count ()) &&
578
0
          (arg1.deltas.length == t->gains.length))))
579
0
      {
580
0
  env.set_error ();
581
0
  return;
582
0
      }
583
0
    }
584
585
0
    str_encoder_t encoder (param.flatStr);
586
0
    unsigned k_new = t->alive () ? t->matrix.length : 0;
587
588
0
    if (!k_new)
589
0
    {
590
      /* All of this VarData's regions died; blends dissolve into statics. */
591
0
      for (unsigned int j = 0; j < n; j++)
592
0
      {
593
0
  const blend_arg_t &arg1 = env.argStack[i + j];
594
0
  number_t v;
595
0
  v.set_real (arg1.to_real () + round (instancer.fold_deltas (ivs, arg1.deltas.as_array ())));
596
0
  encoder.encode_num_cs (v);
597
0
      }
598
0
      return;
599
0
    }
600
601
    /* Emit in chunks so the output charstring stays within the argument
602
     * stack limit: values already emitted for this op, plus this chunk's
603
     * defaults, deltas and count, must not exceed 513. */
604
0
    unsigned done = 0;
605
0
    while (done < n)
606
0
    {
607
0
      unsigned avail = 512 > (i + done) ? 512 - (i + done) : 0;
608
0
      unsigned chunk = hb_min (avail / (k_new + 1), n - done);
609
0
      if (unlikely (!chunk))
610
0
      {
611
0
  env.set_error ();
612
0
  return;
613
0
      }
614
615
0
      for (unsigned int j = done; j < done + chunk; j++)
616
0
      {
617
0
  const blend_arg_t &arg1 = env.argStack[i + j];
618
0
  number_t v;
619
0
  v.set_real (arg1.to_real () + round (instancer.fold_deltas (ivs, arg1.deltas.as_array ())));
620
0
  encoder.encode_num_cs (v);
621
0
      }
622
0
      for (unsigned int j = done; j < done + chunk; j++)
623
0
      {
624
0
  const blend_arg_t &arg1 = env.argStack[i + j];
625
0
  for (unsigned r = 0; r < k_new; r++)
626
0
  {
627
0
    number_t v;
628
0
    v.set_int ((int) round (t->transform_delta (r, arg1.deltas.as_array ())));
629
0
    encoder.encode_num_cs (v);
630
0
  }
631
0
      }
632
0
      encoder.encode_int (chunk);
633
0
      encoder.encode_op (OpCode_blendcs);
634
0
      done += chunk;
635
0
    }
636
0
    param.emitted_blend = true;
637
0
  }
638
639
  /* Partial instancing: prepend a vsindex op when the charstring's
640
   * (remapped) ivs differs from its FD's (remapped) private-dict ivs. */
641
  static void emit_vsindex_prefix (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
642
0
  {
643
0
    if (!param.emitted_blend) return;
644
0
    const cff2_instancing_plan_t &instancer = *param.instancer;
645
0
    unsigned new_ivs = instancer.remap_ivs (env.get_ivs ());
646
0
    unsigned fd_ivs = instancer.remap_ivs (env.get_orig_ivs ());
647
0
    if (new_ivs == fd_ivs) return;
648
649
0
    str_buff_t prefix;
650
0
    str_encoder_t enc (prefix);
651
0
    enc.encode_int ((int) new_ivs);
652
0
    enc.encode_op (OpCode_vsindexcs);
653
654
0
    str_buff_t &buf = param.flatStr;
655
0
    unsigned old_len = buf.length;
656
0
    if (unlikely (prefix.in_error () || !buf.resize (old_len + prefix.length)))
657
0
    {
658
0
      env.set_error ();
659
0
      return;
660
0
    }
661
0
    memmove (buf.arrayZ + prefix.length, buf.arrayZ, old_len);
662
0
    hb_memcpy (buf.arrayZ, prefix.arrayZ, prefix.length);
663
0
  }
664
665
  static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
666
0
  {
667
0
    switch (op)
668
0
    {
669
0
      case OpCode_return:
670
0
      case OpCode_endchar:
671
0
  return;
672
0
      default:
673
0
  str_encoder_t  encoder (param.flatStr);
674
0
  encoder.encode_op (op);
675
0
    }
676
0
  }
677
678
  static void flush_hintmask (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
679
0
  {
680
0
    SUPER::flush_hintmask (op, env, param);
681
    /* Preserve hintmask payload in captured commands for specializer re-encoding. */
682
0
    if (param.commands && !param.drop_hints && param.commands->length > 0)
683
0
    {
684
0
      auto &cmd = param.commands->tail ();
685
0
      if (cmd.op == op)
686
0
      {
687
0
        cmd.mask_bytes.resize (env.hintmask_size);
688
0
        if (unlikely (cmd.mask_bytes.in_error ()))
689
0
        {
690
0
          env.set_error ();
691
0
          return;
692
0
        }
693
0
        for (unsigned int i = 0; i < env.hintmask_size; i++)
694
0
          cmd.mask_bytes[i] = env.str_ref[i];
695
0
      }
696
0
    }
697
0
    if (!param.drop_hints)
698
0
    {
699
0
      str_encoder_t  encoder (param.flatStr);
700
0
      for (unsigned int i = 0; i < env.hintmask_size; i++)
701
0
  encoder.encode_byte (env.str_ref[i]);
702
0
    }
703
0
  }
704
705
  private:
706
  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
707
  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;
708
};
709
710
struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
711
{
712
  static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
713
0
  {
714
0
    switch (op) {
715
716
0
      case OpCode_return:
717
0
  param.current_parsed_str->flush_coalesced (env.str_ref);
718
0
  param.current_parsed_str->set_parsed ();
719
0
  env.return_from_subr ();
720
0
  param.set_current_str (env, false);
721
0
  break;
722
723
0
      case OpCode_endchar:
724
0
  param.current_parsed_str->flush_coalesced (env.str_ref);
725
0
  param.current_parsed_str->set_parsed ();
726
0
  SUPER::process_op (op, env, param);
727
0
  break;
728
729
0
      case OpCode_callsubr:
730
0
  process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
731
0
  break;
732
733
0
      case OpCode_callgsubr:
734
0
  process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
735
0
  break;
736
737
0
      default:
738
0
  SUPER::process_op (op, env, param);
739
0
  param.current_parsed_str->add_op (op, env.str_ref);
740
0
  break;
741
0
    }
742
0
  }
743
744
  protected:
745
  static void process_call_subr (op_code_t op, cs_type_t type,
746
         cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
747
         cff2_biased_subrs_t& subrs, hb_set_t *closure)
748
0
  {
749
0
    byte_str_ref_t    str_ref = env.str_ref;
750
0
    env.call_subr (subrs, type);
751
0
    param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
752
0
    closure->add (env.context.subr_num);
753
0
    param.set_current_str (env, true);
754
0
  }
755
756
  private:
757
  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
758
};
759
760
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>
761
{
762
  cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
763
0
    : subr_subsetter_t (acc_, plan_) {}
764
765
  static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
766
0
  {
767
    /* vsindex is inserted at the beginning of the charstring as necessary */
768
0
    if (env.seen_vsindex ())
769
0
    {
770
0
      number_t  ivs;
771
0
      ivs.set_int ((int)env.get_ivs ());
772
0
      charstring.set_prefix (ivs, OpCode_vsindexcs);
773
0
    }
774
0
  }
775
};
776
777
struct cff2_private_blend_encoder_param_t
778
{
779
  cff2_private_blend_encoder_param_t (hb_serialize_context_t *c,
780
              const CFF2ItemVariationStore *varStore,
781
              hb_array_t<int> normalized_coords) :
782
0
    c (c), varStore (varStore), normalized_coords (normalized_coords) {}
783
784
0
  void init () {}
785
786
  void process_blend ()
787
0
  {
788
0
    if (!seen_blend)
789
0
    {
790
0
      region_count = varStore->varStore.get_region_index_count (ivs);
791
0
      if (unlikely (!scalars.resize_exact (region_count)))
792
0
      {
793
0
  if (c) c->err (HB_SERIALIZE_ERROR_OTHER);
794
0
  seen_blend = true;
795
0
  return;
796
0
      }
797
0
      varStore->varStore.get_region_scalars (ivs, normalized_coords.arrayZ, normalized_coords.length,
798
0
               &scalars[0], region_count);
799
0
      seen_blend = true;
800
0
    }
801
0
  }
802
803
  double blend_deltas (hb_array_t<const number_t> deltas) const
804
0
  {
805
0
    double v = 0;
806
0
    if (likely (scalars.length == deltas.length))
807
0
    {
808
0
      unsigned count = scalars.length;
809
0
      for (unsigned i = 0; i < count; i++)
810
0
  v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
811
0
    }
812
0
    return v;
813
0
  }
814
815
816
  hb_serialize_context_t *c = nullptr;
817
  bool seen_blend = false;
818
  unsigned ivs = 0;
819
  unsigned region_count = 0;
820
  hb_vector_t<float> scalars;
821
  const  CFF2ItemVariationStore *varStore = nullptr;
822
  hb_array_t<int> normalized_coords;
823
824
  /* CFF2 partial instancing: when set, blends are rewritten against the
825
   * instanced variation store instead of being flattened. */
826
  const cff2_instancing_plan_t *instancer = nullptr;
827
  /* Error-compensated fold rounding state, per dict operator. */
828
  double fold_exact = 0.;
829
  double fold_emitted = 0.;
830
};
831
832
struct cff2_private_dict_blend_opset_t : dict_opset_t
833
{
834
  static void process_arg_blend (cff2_private_blend_encoder_param_t& param,
835
         number_t &arg,
836
         const hb_array_t<const number_t> blends,
837
         unsigned n, unsigned i)
838
0
  {
839
0
    arg.set_int (round (arg.to_real () + param.blend_deltas (blends)));
840
0
  }
841
842
  /* The fold added to a value, error-compensated across the operator: most
843
   * blended private-dict values (BlueValues, StemSnap*, ...) are
844
   * delta-encoded, so independently rounded folds would accumulate in the
845
   * decoded absolutes; keeping the emitted cumulative fold equal to the
846
   * rounded exact cumulative fold bounds every absolute within ±0.5. */
847
  static double fold (cff2_private_blend_encoder_param_t& param,
848
          const hb_array_t<const number_t> blends)
849
0
  {
850
0
    double f = param.blend_deltas (blends);
851
0
    double emit = round (param.fold_exact + f) - param.fold_emitted;
852
0
    param.fold_exact += f;
853
0
    param.fold_emitted += emit;
854
0
    return emit;
855
0
  }
856
857
  /* Partial instancing: emit the args preceding this blend group, then the
858
   * group itself rewritten against the instanced store, and clear the
859
   * stack.  The op's remaining args are flushed at process_op time. */
860
  static void rewrite_blends (cff2_priv_dict_interp_env_t& env,
861
            cff2_private_blend_encoder_param_t& param,
862
            unsigned start, unsigned n, unsigned k)
863
0
  {
864
0
    const cff2_instancing_plan_t &instancer = *param.instancer;
865
0
    const cff2_instancing_plan_t::vardata_transform_t *t = instancer.get_transform (param.ivs);
866
0
    if (unlikely (!t || t->gains.length != k))
867
0
    {
868
0
      env.set_error ();
869
0
      return;
870
0
    }
871
872
0
    str_buff_t str;
873
0
    str_encoder_t encoder (str);
874
875
0
    for (unsigned idx = 0; idx < start; idx++)
876
0
      encoder.encode_num_tp (env.argStack[idx]);
877
878
0
    unsigned k_new = t->alive () ? t->matrix.length : 0;
879
0
    if (!k_new)
880
0
    {
881
      /* This VarData died (or is unused by any retained charstring, which
882
       * can happen for FDs kept only for retain-gids holes); blends
883
       * dissolve into statics. */
884
0
      for (unsigned j = 0; j < n; j++)
885
0
      {
886
0
  const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (j * k), k);
887
0
  number_t v;
888
0
  v.set_real (env.argStack[start + j].to_real () + fold (param, blends));
889
0
  encoder.encode_num_tp (v);
890
0
      }
891
0
    }
892
0
    else
893
0
    {
894
0
      unsigned done = 0;
895
0
      while (done < n)
896
0
      {
897
0
  unsigned avail = 512 > (start + done) ? 512 - (start + done) : 0;
898
0
  unsigned chunk = hb_min (avail / (k_new + 1), n - done);
899
0
  if (unlikely (!chunk))
900
0
  {
901
0
    env.set_error ();
902
0
    return;
903
0
  }
904
905
0
  for (unsigned j = done; j < done + chunk; j++)
906
0
  {
907
0
    const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (j * k), k);
908
0
    number_t v;
909
0
    v.set_real (env.argStack[start + j].to_real () + fold (param, blends));
910
0
    encoder.encode_num_tp (v);
911
0
  }
912
0
  for (unsigned j = done; j < done + chunk; j++)
913
0
  {
914
0
    const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (j * k), k);
915
0
    for (unsigned r = 0; r < k_new; r++)
916
0
    {
917
0
      number_t v;
918
0
      v.set_int ((int) round (t->transform_delta (r, blends)));
919
0
      encoder.encode_num_tp (v);
920
0
    }
921
0
  }
922
0
  encoder.encode_int (chunk);
923
0
  encoder.encode_op (OpCode_blenddict);
924
0
  done += chunk;
925
0
      }
926
0
    }
927
928
0
    auto bytes = str.as_bytes ();
929
0
    param.c->embed (&bytes, bytes.length);
930
931
0
    env.clear_args ();
932
0
  }
933
934
  static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
935
0
  {
936
0
    unsigned int n, k;
937
938
0
    param.process_blend ();
939
0
    k = param.region_count;
940
0
    n = env.argStack.pop_uint ();
941
942
0
    unsigned count = env.argStack.get_count ();
943
0
    unsigned int total;
944
0
    if (unlikely (hb_unsigned_mul_overflows (k + 1, n, &total) || total > count))
945
0
    {
946
0
      env.set_error ();
947
0
      return;
948
0
    }
949
950
    /* copy the blend values into blend array of the default values */
951
0
    unsigned int start = count - total;
952
    /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
953
0
    if (unlikely (start > count))
954
0
    {
955
0
      env.set_error ();
956
0
      return;
957
0
    }
958
959
0
    if (param.instancer && k)
960
0
    {
961
0
      rewrite_blends (env, param, start, n, k);
962
0
      return;
963
0
    }
964
965
0
    for (unsigned int i = 0; i < n; i++)
966
0
    {
967
0
      const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (i * k), k);
968
0
      process_arg_blend (param, env.argStack[start + i], blends, n, i);
969
0
    }
970
971
    /* pop off blend values leaving default values now adorned with blend values */
972
0
    env.argStack.pop (k * n);
973
0
  }
974
975
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
976
0
  {
977
0
    switch (op) {
978
0
      case OpCode_StdHW:
979
0
      case OpCode_StdVW:
980
0
      case OpCode_BlueScale:
981
0
      case OpCode_BlueShift:
982
0
      case OpCode_BlueFuzz:
983
0
      case OpCode_ExpansionFactor:
984
0
      case OpCode_LanguageGroup:
985
0
      case OpCode_BlueValues:
986
0
      case OpCode_OtherBlues:
987
0
      case OpCode_FamilyBlues:
988
0
      case OpCode_FamilyOtherBlues:
989
0
      case OpCode_StemSnapH:
990
0
      case OpCode_StemSnapV:
991
0
  break;
992
0
      case OpCode_vsindexdict:
993
0
  env.process_vsindex ();
994
0
  param.ivs = env.get_ivs ();
995
0
  if (param.instancer)
996
0
  {
997
    /* Re-emit with the remapped vsindex; 0 is the dict default. */
998
0
    unsigned new_ivs = param.instancer->remap_ivs (param.ivs);
999
0
    if (new_ivs)
1000
0
    {
1001
0
      str_buff_t str;
1002
0
      str_encoder_t encoder (str);
1003
0
      encoder.encode_int ((int) new_ivs);
1004
0
      encoder.encode_op (OpCode_vsindexdict);
1005
0
      auto bytes = str.as_bytes ();
1006
0
      param.c->embed (&bytes, bytes.length);
1007
0
    }
1008
0
  }
1009
0
  env.clear_args ();
1010
0
  return;
1011
0
      case OpCode_blenddict:
1012
0
  process_blend (env, param);
1013
0
  return;
1014
1015
0
      default:
1016
0
  dict_opset_t::process_op (op, env);
1017
0
  if (!env.argStack.is_empty ()) return;
1018
0
  break;
1019
0
    }
1020
1021
0
    if (unlikely (env.in_error ())) return;
1022
1023
    // Write args then op
1024
1025
0
    str_buff_t str;
1026
0
    str_encoder_t encoder (str);
1027
1028
0
    unsigned count = env.argStack.get_count ();
1029
0
    for (unsigned i = 0; i < count; i++)
1030
0
      encoder.encode_num_tp (env.argStack[i]);
1031
1032
0
    encoder.encode_op (op);
1033
1034
0
    auto bytes = str.as_bytes ();
1035
0
    param.c->embed (&bytes, bytes.length);
1036
1037
0
    env.clear_args ();
1038
0
    param.fold_exact = param.fold_emitted = 0.;
1039
0
  }
1040
};
1041
1042
struct cff2_private_dict_op_serializer_t : op_serializer_t
1043
{
1044
  cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_,
1045
             const CFF::CFF2ItemVariationStore* varStore_,
1046
             hb_array_t<int> normalized_coords_,
1047
             const cff2_instancing_plan_t *instancer_ = nullptr)
1048
0
    : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_),
1049
0
      param (nullptr, varStore_, normalized_coords_)
1050
0
  { param.instancer = instancer_; }
1051
1052
  bool serialize (hb_serialize_context_t *c,
1053
      const op_str_t &opstr,
1054
      objidx_t subrs_link) const
1055
0
  {
1056
0
    TRACE_SERIALIZE (this);
1057
1058
0
    if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
1059
0
      return_trace (true);
1060
1061
0
    if (opstr.op == OpCode_Subrs)
1062
0
    {
1063
0
      if (desubroutinize || !subrs_link)
1064
0
  return_trace (true);
1065
0
      else
1066
0
  return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
1067
0
    }
1068
1069
0
    if (pinned || param.instancer)
1070
0
    {
1071
      /* Reinterpret opstr and process blends.  The param persists across
1072
       * this dict's opstrs, so that an ivs set by a vsindexdict opstr is
1073
       * still in effect when a later opstr's blends are processed. */
1074
0
      cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)};
1075
0
      param.c = c;
1076
0
      dict_interpreter_t<cff2_private_dict_blend_opset_t, cff2_private_blend_encoder_param_t, cff2_priv_dict_interp_env_t> interp (env);
1077
0
      return_trace (interp.interpret (param));
1078
0
    }
1079
1080
0
    return_trace (copy_opstr (c, opstr));
1081
0
  }
1082
1083
  protected:
1084
  const bool desubroutinize;
1085
  const bool drop_hints;
1086
  const bool pinned;
1087
  mutable cff2_private_blend_encoder_param_t param;
1088
};
1089
1090
1091
1092
namespace OT {
1093
struct cff2_subset_plan
1094
{
1095
  bool create (const OT::cff2::accelerator_subset_t &acc,
1096
        hb_subset_plan_t *plan)
1097
0
  {
1098
    /* make sure notdef is first */
1099
0
    hb_codepoint_t old_glyph;
1100
0
    if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
1101
1102
0
    num_glyphs = plan->num_output_glyphs ();
1103
0
    orig_fdcount = acc.fdArray->count;
1104
1105
0
    drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
1106
0
    bool instancing_requested = (bool) plan->normalized_coords;
1107
0
    pinned = instancing_requested && plan->all_axes_pinned;
1108
0
    normalized_coords = plan->normalized_coords;
1109
0
    head_maxp_info = plan->head_maxp_info;
1110
0
    hmtx_map = &plan->hmtx_map;
1111
0
    desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE ||
1112
0
         instancing_requested; // For instancing we need this path
1113
1114
    /* Partial instancing: some axes pinned or limited, others kept. */
1115
0
    partial_instancing = instancing_requested && !pinned &&
1116
0
       acc.varStore != &Null (CFF2ItemVariationStore);
1117
0
    if (partial_instancing &&
1118
0
  unlikely (!instancing.create (acc.varStore, plan)))
1119
0
      return false;
1120
0
    axis_tags = &plan->axis_tags;
1121
1122
    /* Enable command capture for CFF2→CFF1 conversion (for specialization) */
1123
0
    capture_commands = pinned;
1124
1125
 #ifdef HB_EXPERIMENTAL_API
1126
    min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
1127
 #else
1128
0
    min_charstrings_off_size = 0;
1129
0
 #endif
1130
1131
0
    if (desubroutinize)
1132
0
    {
1133
0
      if (partial_instancing)
1134
0
      {
1135
  /* Pass 1: collect which VarDatas the retained blends reference, so
1136
   * unused ones get pruned and the survivors renumbered. */
1137
0
  hb_set_t used_fds;
1138
0
  for (unsigned int i = 0; i < num_glyphs; i++)
1139
0
  {
1140
0
    hb_codepoint_t glyph;
1141
0
    if (!plan->old_gid_for_new_gid (i, &glyph))
1142
0
      continue;
1143
0
    const hb_ubytes_t str = (*acc.charStrings)[glyph];
1144
0
    unsigned int fd = acc.fdSelect->get_fd (glyph);
1145
0
    if (unlikely (fd >= acc.fdCount))
1146
0
      return false;
1147
0
    used_fds.add (fd);
1148
1149
0
    cff2_cs_interp_env_t<blend_arg_t> env (str, acc, fd);
1150
0
    cs_interpreter_t<cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_usage_t, cff2_usage_param_t> interp (env);
1151
0
    cff2_usage_param_t param;
1152
0
    param.instancing = &instancing;
1153
0
    param.drop_hints = drop_hints;
1154
0
    if (unlikely (!interp.interpret (param)))
1155
0
      return false;
1156
0
  }
1157
0
  for (unsigned fd : used_fds)
1158
0
  {
1159
0
    cff2_usage_param_t param;
1160
0
    param.instancing = &instancing;
1161
0
    unsigned count = acc.privateDicts[fd].get_count ();
1162
0
    for (unsigned j = 0; j < count; j++)
1163
0
    {
1164
0
      const op_str_t &opstr = acc.privateDicts[fd][j];
1165
0
      if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
1166
0
        continue;
1167
0
      cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)};
1168
0
      dict_interpreter_t<cff2_private_dict_usage_opset_t, cff2_usage_param_t, cff2_priv_dict_interp_env_t> interp (env);
1169
0
      if (unlikely (!interp.interpret (param)))
1170
0
        return false;
1171
0
    }
1172
0
  }
1173
0
  if (unlikely (!instancing.finalize ()))
1174
0
    return false;
1175
1176
  /* Pass 2: like subr_flattener_t, but blends stay symbolic (no coords
1177
   * fed to the env) and get rewritten against the instanced store. */
1178
0
  if (!subset_charstrings.resize_exact (num_glyphs))
1179
0
    return false;
1180
0
  for (unsigned int i = 0; i < num_glyphs; i++)
1181
0
  {
1182
0
    hb_codepoint_t glyph;
1183
0
    if (!plan->old_gid_for_new_gid (i, &glyph))
1184
0
      continue;
1185
0
    const hb_ubytes_t str = (*acc.charStrings)[glyph];
1186
0
    unsigned int fd = acc.fdSelect->get_fd (glyph);
1187
0
    if (unlikely (fd >= acc.fdCount))
1188
0
      return false;
1189
1190
0
    cff2_cs_interp_env_t<blend_arg_t> env (str, acc, fd);
1191
0
    cs_interpreter_t<cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t, flatten_param_t> interp (env);
1192
0
    flatten_param_t param = {
1193
0
      subset_charstrings.arrayZ[i],
1194
0
      (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING),
1195
0
      plan
1196
0
    };
1197
0
    param.instancer = &instancing;
1198
0
    if (unlikely (!interp.interpret (param)))
1199
0
      return false;
1200
0
  }
1201
0
      }
1202
0
      else
1203
0
      {
1204
      /* Flatten global & local subrs */
1205
0
      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
1206
0
        flattener(acc, plan);
1207
1208
      /* Enable command capture if requested (for specialization) */
1209
0
      if (capture_commands)
1210
0
      {
1211
0
  if (!charstring_commands.resize_exact (num_glyphs))
1212
0
    return false;
1213
1214
0
  if (!flattener.flatten (subset_charstrings, &charstring_commands))
1215
0
    return false;
1216
0
      }
1217
0
      else
1218
0
      {
1219
0
  if (!flattener.flatten (subset_charstrings))
1220
0
    return false;
1221
0
      }
1222
0
      }
1223
0
    }
1224
0
    else
1225
0
    {
1226
0
      cff2_subr_subsetter_t subr_subsetter (acc, plan);
1227
1228
      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
1229
0
      if (!subr_subsetter.subset ())
1230
0
  return false;
1231
1232
      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
1233
0
      if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned))
1234
0
  return false;
1235
1236
0
      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
1237
0
  return false;
1238
1239
      /* local subrs */
1240
0
      if (!subset_localsubrs.resize (orig_fdcount))
1241
0
  return false;
1242
0
      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
1243
0
      {
1244
0
  subset_localsubrs[fd].init ();
1245
0
  if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
1246
0
    return false;
1247
0
      }
1248
0
    }
1249
1250
    /* FDSelect */
1251
0
    if (acc.fdSelect != &Null (CFF2FDSelect))
1252
0
    {
1253
0
      if (unlikely (!hb_plan_subset_cff_fdselect (plan,
1254
0
              orig_fdcount,
1255
0
              *(const FDSelect *)acc.fdSelect,
1256
0
              subset_fdcount,
1257
0
              subset_fdselect_size,
1258
0
              subset_fdselect_format,
1259
0
              subset_fdselect_ranges,
1260
0
              fdmap)))
1261
0
  return false;
1262
0
    }
1263
0
    else
1264
0
      fdmap.identity (1);
1265
1266
0
    return true;
1267
0
  }
1268
1269
  cff2_sub_table_info_t info;
1270
1271
  unsigned int    num_glyphs;
1272
  unsigned int    orig_fdcount = 0;
1273
  unsigned int    subset_fdcount = 1;
1274
  unsigned int    subset_fdselect_size = 0;
1275
  unsigned int    subset_fdselect_format = 0;
1276
  bool            pinned = false;
1277
  hb_vector_t<code_pair_t>   subset_fdselect_ranges;
1278
1279
  hb_inc_bimap_t   fdmap;
1280
1281
  str_buff_vec_t      subset_charstrings;
1282
  str_buff_vec_t      subset_globalsubrs;
1283
  hb_vector_t<str_buff_vec_t> subset_localsubrs;
1284
1285
  bool      drop_hints = false;
1286
  bool      desubroutinize = false;
1287
1288
  bool      partial_instancing = false;
1289
  cff2_instancing_plan_t instancing;
1290
  const hb_vector_t<hb_tag_t> *axis_tags = nullptr;
1291
1292
  unsigned  min_charstrings_off_size = 0;
1293
1294
  hb_array_t<int> normalized_coords; // For instantiation
1295
  head_maxp_info_t head_maxp_info;  // For FontBBox
1296
  const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>> *hmtx_map; // For widths
1297
1298
  // Width optimization results (for CFF1 conversion)
1299
  unsigned default_width = 0;
1300
  unsigned nominal_width = 0;
1301
1302
  // Command capture for specialization (CFF2→CFF1 conversion)
1303
  bool capture_commands = false;
1304
  hb_vector_t<hb_vector_t<cs_command_t>> charstring_commands;
1305
};
1306
} // namespace OT
1307
1308
/*
1309
 * CFF2 to CFF1 Converter Implementation
1310
 */
1311
1312
#include "hb-cff-width-optimizer.hh"
1313
#include "hb-cff-specializer.hh"
1314
1315
/* Serialize charstrings using CFF1 format with widths */
1316
static bool
1317
_serialize_cff1_charstrings (hb_serialize_context_t *c,
1318
                             OT::cff2_subset_plan &plan,
1319
                             unsigned default_width,
1320
                             unsigned nominal_width)
1321
0
{
1322
0
  c->push ();
1323
1324
  // CFF1 requires:
1325
  // 1. Width at the beginning (if != defaultWidthX)
1326
  // 2. endchar at the end
1327
0
  str_buff_vec_t cff1_charstrings;
1328
0
  if (unlikely (!cff1_charstrings.resize (plan.subset_charstrings.length)))
1329
0
  {
1330
0
    c->pop_discard ();
1331
0
    return false;
1332
0
  }
1333
1334
0
  for (unsigned i = 0; i < plan.subset_charstrings.length; i++)
1335
0
  {
1336
    // Get width for this glyph from hmtx_map
1337
0
    unsigned width = 0;
1338
0
    if (plan.hmtx_map->has (i))
1339
0
      width = plan.hmtx_map->get (i).first;
1340
1341
    // Encode width if different from default
1342
0
    str_encoder_t encoder (cff1_charstrings[i]);
1343
0
    if (width != default_width)
1344
0
    {
1345
0
      int delta = (int) width - (int) nominal_width;
1346
0
      encoder.encode_int (delta);
1347
0
    }
1348
1349
    // Use specialized commands if available, otherwise use binary
1350
0
    if (plan.capture_commands && i < plan.charstring_commands.length &&
1351
0
        plan.charstring_commands[i].length > 0)
1352
0
    {
1353
      // Specialize and encode commands
1354
0
      auto &commands = plan.charstring_commands[i];
1355
0
      CFF::specialize_commands (commands, 48);  /* maxstack=48 for CFF1 */
1356
0
      if (unlikely (!CFF::encode_commands (commands, cff1_charstrings[i])))
1357
0
      {
1358
0
        c->pop_discard ();
1359
0
        return false;
1360
0
      }
1361
0
    }
1362
0
    else
1363
0
    {
1364
      // Use binary CharString
1365
0
      const str_buff_t &cs = plan.subset_charstrings[i];
1366
0
      for (unsigned j = 0; j < cs.length; j++)
1367
0
        cff1_charstrings[i].push (cs[j]);
1368
0
    }
1369
1370
    // Check if it already ends with endchar (0x0e) or return (0x0b)
1371
0
    if (cff1_charstrings[i].length == 0 ||
1372
0
        (cff1_charstrings[i].tail () != 0x0e && cff1_charstrings[i].tail () != 0x0b))
1373
0
    {
1374
      // Append endchar operator
1375
0
      if (unlikely (!cff1_charstrings[i].push_or_fail (0x0e)))
1376
0
      {
1377
0
        c->pop_discard ();
1378
0
        return false;
1379
0
      }
1380
0
    }
1381
0
  }
1382
1383
0
  unsigned data_size = 0;
1384
0
  unsigned total_size = CFF1CharStrings::total_size (cff1_charstrings, &data_size);
1385
0
  if (unlikely (!c->start_zerocopy (total_size)))
1386
0
  {
1387
0
    c->pop_discard ();
1388
0
    return false;
1389
0
  }
1390
1391
0
  auto *cs = c->start_embed<CFF1CharStrings> ();
1392
0
  if (unlikely (!cs->serialize (c, cff1_charstrings)))
1393
0
  {
1394
0
    c->pop_discard ();
1395
0
    return false;
1396
0
  }
1397
1398
0
  plan.info.char_strings_link = c->pop_pack (false);
1399
0
  return true;
1400
0
}
1401
1402
/* Serialize CID Charset (format 2 range: gid 0-N -> cid 0-N) */
1403
static bool
1404
_serialize_cff1_charset (hb_serialize_context_t *c,
1405
                         unsigned int num_glyphs,
1406
                         objidx_t &charset_link)
1407
0
{
1408
  // For CID fonts, create a simple identity charset
1409
  // Format 2: one range covering all glyphs (except .notdef)
1410
0
  c->push ();
1411
1412
0
  auto *charset = c->start_embed<Charset> ();
1413
0
  if (unlikely (!charset))
1414
0
  {
1415
0
    c->pop_discard ();
1416
0
    return false;
1417
0
  }
1418
1419
  // Create a single range for CID 1 to num_glyphs-1
1420
0
  hb_vector_t<code_pair_t> ranges;
1421
0
  if (num_glyphs > 1)
1422
0
  {
1423
0
    code_pair_t range;
1424
0
    range.code = 1;  // first CID
1425
0
    range.glyph = num_glyphs - 2;  // nLeft (covers glyphs 1 to num_glyphs-1)
1426
0
    ranges.push (range);
1427
0
  }
1428
1429
0
  if (unlikely (!charset->serialize (c, 2, num_glyphs, ranges)))
1430
0
  {
1431
0
    c->pop_discard ();
1432
0
    return false;
1433
0
  }
1434
1435
0
  charset_link = c->pop_pack ();
1436
0
  return true;
1437
0
}
1438
1439
/* CFF2 to CFF1 serialization */
1440
namespace CFF {
1441
1442
bool
1443
serialize_cff2_to_cff1 (hb_serialize_context_t *c,
1444
                        OT::cff2_subset_plan &plan,
1445
                        const cff2_top_dict_values_t &cff2_topDict,
1446
                        const OT::cff2::accelerator_subset_t &acc)
1447
0
{
1448
0
  TRACE_SERIALIZE (this);
1449
1450
  /*
1451
   * CFF1 Serialization Order (reverse, as HarfBuzz packs from end):
1452
   * 1. CharStrings
1453
   * 2. Private DICs & Local Subrs
1454
   * 3. FDArray
1455
   * 4. FDSelect
1456
   * 5. Charset
1457
   * 6. Global Subrs
1458
   * 7. String INDEX
1459
   * 8. Top DICT INDEX
1460
   * 9. Name INDEX
1461
   * 10. Header
1462
   */
1463
1464
  // 0. Optimize width encoding (for all FDs)
1465
0
  {
1466
    // Collect widths from hmtx_map
1467
0
    hb_vector_t<unsigned> widths;
1468
0
    widths.alloc (plan.num_glyphs);
1469
1470
0
    for (unsigned gid = 0; gid < plan.num_glyphs; gid++)
1471
0
    {
1472
0
      unsigned width = 0;
1473
0
      if (plan.hmtx_map->has (gid))
1474
0
        width = plan.hmtx_map->get (gid).first;
1475
0
      widths.push (width);
1476
0
    }
1477
1478
    // Optimize defaultWidthX and nominalWidthX
1479
0
    CFF::optimize_widths (widths, plan.default_width, plan.nominal_width);
1480
0
  }
1481
1482
  // 1. CharStrings (with widths prepended)
1483
0
  if (!_serialize_cff1_charstrings (c, plan, plan.default_width, plan.nominal_width))
1484
0
    return_trace (false);
1485
1486
  // 2. Private DICs & Local Subrs (same as CFF2)
1487
0
  hb_vector_t<table_info_t> private_dict_infos;
1488
0
  if (unlikely (!private_dict_infos.resize (plan.subset_fdcount)))
1489
0
    return_trace (false);
1490
1491
0
  for (int i = (int)acc.privateDicts.length; --i >= 0;)
1492
0
  {
1493
0
    if (plan.fdmap.has (i))
1494
0
    {
1495
0
      objidx_t subrs_link = 0;
1496
1497
0
      if (plan.subset_localsubrs[i].length > 0)
1498
0
      {
1499
0
        auto *dest = c->push<CFF1Subrs> ();
1500
0
        if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
1501
0
          subrs_link = c->pop_pack (false);
1502
0
        else
1503
0
        {
1504
0
          c->pop_discard ();
1505
0
          return_trace (false);
1506
0
        }
1507
0
      }
1508
1509
0
      auto *pd = c->push<PrivateDict> ();
1510
      // Use the CFF2 Private DICT serializer which instantiates blends when pinned=true
1511
0
      cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
1512
0
                                                  acc.varStore, plan.normalized_coords);
1513
0
      if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
1514
0
      {
1515
        // Add defaultWidthX and nominalWidthX for CFF1
1516
0
        str_buff_t width_ops;
1517
0
        str_encoder_t encoder (width_ops);
1518
0
        encoder.encode_int (plan.default_width);
1519
0
        encoder.encode_op (OpCode_defaultWidthX);
1520
0
        encoder.encode_int (plan.nominal_width);
1521
0
        encoder.encode_op (OpCode_nominalWidthX);
1522
1523
0
        if (!encoder.in_error () && c->embed (width_ops.as_bytes ().arrayZ, width_ops.length))
1524
0
        {
1525
0
          unsigned fd = plan.fdmap[i];
1526
0
          private_dict_infos[fd].size = c->length ();
1527
0
          private_dict_infos[fd].link = c->pop_pack ();
1528
0
        }
1529
0
        else
1530
0
        {
1531
0
          c->pop_discard ();
1532
0
          return_trace (false);
1533
0
        }
1534
0
      }
1535
0
      else
1536
0
      {
1537
0
        c->pop_discard ();
1538
0
        return_trace (false);
1539
0
      }
1540
0
    }
1541
0
  }
1542
1543
  // 3. FDArray - serialize CFF2 font dicts as CFF1
1544
0
  {
1545
0
    auto *fda = c->push<FDArray<HBUINT16>> ();
1546
0
    cff_font_dict_op_serializer_t fontSzr;
1547
0
    auto it =
1548
0
    + hb_zip (+ hb_iter (acc.fontDicts)
1549
0
              | hb_filter ([&] (const cff2_font_dict_values_t &_)
1550
0
                { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
1551
0
              hb_iter (private_dict_infos))
1552
0
    ;
1553
    // Explicitly specify template parameters: DICTVAL, INFO
1554
0
    bool success = fda->serialize<cff2_font_dict_values_t, table_info_t> (c, it, fontSzr);
1555
0
    if (success)
1556
0
      plan.info.fd_array_link = c->pop_pack (false);
1557
0
    else
1558
0
    {
1559
0
      c->pop_discard ();
1560
0
      return_trace (false);
1561
0
    }
1562
0
  }
1563
1564
  // 4. FDSelect (required in CFF1 CID-keyed fonts)
1565
  // CFF1 requires FDSelect for all CID-keyed fonts, even with just one FD
1566
  // CFF2 makes it optional when there's only one FD
1567
0
  if (acc.fdSelect != &Null (CFF2FDSelect))
1568
0
  {
1569
0
    c->push ();
1570
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs,
1571
0
                                          *(const FDSelect *)acc.fdSelect,
1572
0
                                          plan.orig_fdcount,
1573
0
                                          plan.subset_fdselect_format,
1574
0
                                          plan.subset_fdselect_size,
1575
0
                                          plan.subset_fdselect_ranges)))
1576
0
      plan.info.fd_select.link = c->pop_pack ();
1577
0
    else
1578
0
    {
1579
0
      c->pop_discard ();
1580
0
      return_trace (false);
1581
0
    }
1582
0
  }
1583
0
  else
1584
0
  {
1585
    // Create a range-based FDSelect3 mapping all glyphs to FD 0
1586
    // Format: format(1) + nRanges(2) + range(3) + sentinel(2) = 8 bytes
1587
0
    c->push ();
1588
1589
    // Format byte
1590
0
    HBUINT8 format;
1591
0
    format = 3;
1592
0
    if (unlikely (!c->embed (format)))
1593
0
    {
1594
0
      c->pop_discard ();
1595
0
      return_trace (false);
1596
0
    }
1597
1598
    // nRanges
1599
0
    HBUINT16 nRanges;
1600
0
    nRanges = 1;
1601
0
    if (unlikely (!c->embed (nRanges)))
1602
0
    {
1603
0
      c->pop_discard ();
1604
0
      return_trace (false);
1605
0
    }
1606
1607
    // Single range: {first: 0, fd: 0}
1608
0
    FDSelect3_Range range;
1609
0
    range.first = 0;
1610
0
    range.fd = 0;
1611
0
    if (unlikely (!c->embed (range)))
1612
0
    {
1613
0
      c->pop_discard ();
1614
0
      return_trace (false);
1615
0
    }
1616
1617
    // Sentinel (number of glyphs)
1618
0
    HBUINT16 sentinel;
1619
0
    sentinel = plan.num_glyphs;
1620
0
    if (unlikely (!c->embed (sentinel)))
1621
0
    {
1622
0
      c->pop_discard ();
1623
0
      return_trace (false);
1624
0
    }
1625
1626
0
    plan.info.fd_select.link = c->pop_pack ();
1627
0
  }
1628
1629
  // 5. Charset (CID charset for identity mapping)
1630
0
  objidx_t charset_link;
1631
0
  if (!_serialize_cff1_charset (c, plan.num_glyphs, charset_link))
1632
0
    return_trace (false);
1633
1634
  // 6. Global Subrs
1635
0
  {
1636
0
    auto *dest = c->push<CFF1Subrs> ();
1637
0
    if (likely (dest->serialize (c, plan.subset_globalsubrs)))
1638
0
      c->pop_pack (false);
1639
0
    else
1640
0
    {
1641
0
      c->pop_discard ();
1642
0
      return_trace (false);
1643
0
    }
1644
0
  }
1645
1646
  // 7. String INDEX - Add "Adobe" and "Identity" for ROS operator
1647
0
  {
1648
0
    const char *adobe_str = "Adobe";
1649
0
    const char *identity_str = "Identity";
1650
0
    unsigned adobe_len = 5;  // strlen("Adobe")
1651
0
    unsigned identity_len = 8;  // strlen("Identity")
1652
1653
    // Build strings array
1654
0
    hb_vector_t<hb_ubytes_t> strings;
1655
0
    strings.alloc (2);
1656
0
    strings.push (hb_ubytes_t ((const unsigned char *) adobe_str, adobe_len));
1657
0
    strings.push (hb_ubytes_t ((const unsigned char *) identity_str, identity_len));
1658
1659
    // Serialize as CFF INDEX
1660
0
    auto *dest = c->push<CFF1Index> ();
1661
0
    if (likely (dest->serialize (c, strings)))
1662
0
      c->pop_pack (false);
1663
0
    else
1664
0
    {
1665
0
      c->pop_discard ();
1666
0
      return_trace (false);
1667
0
    }
1668
0
  }
1669
1670
  // 8. CFF Header
1671
0
  OT::cff1 *cff = c->allocate_min<OT::cff1> ();
1672
0
  if (unlikely (!cff)) return_trace (false);
1673
1674
  /* header */
1675
0
  cff->version.major = 0x01;
1676
0
  cff->version.minor = 0x00;
1677
0
  cff->nameIndex = cff->min_size;
1678
0
  cff->offSize = 4; /* unused? */
1679
1680
  // 9. Name INDEX (single entry)
1681
0
  {
1682
0
    unsigned name_len = strlen (CFF1_DEFAULT_FONT_NAME);
1683
1684
0
    CFF1Index *idx = c->start_embed<CFF1Index> ();
1685
0
    if (unlikely (!idx)) return_trace (false);
1686
1687
0
    if (unlikely (!idx->serialize_header (c, hb_iter (&name_len, 1), name_len)))
1688
0
      return_trace (false);
1689
1690
0
    if (unlikely (!c->embed (CFF1_DEFAULT_FONT_NAME, name_len)))
1691
0
      return_trace (false);
1692
0
  }
1693
1694
  // 10. Top DICT INDEX
1695
0
  {
1696
    // Serialize the Top DICT data first
1697
0
    c->push<TopDict> ();
1698
0
    cff1_from_cff2_top_dict_op_serializer_t topSzr;
1699
1700
    // Serialize ROS first
1701
0
    if (unlikely (!topSzr.serialize_ros (c)))
1702
0
    {
1703
0
      c->pop_discard ();
1704
0
      return_trace (false);
1705
0
    }
1706
1707
    // Serialize FontBBox from head table
1708
0
    {
1709
0
      str_buff_t bbox_buff;
1710
0
      str_encoder_t encoder (bbox_buff);
1711
1712
0
      encoder.encode_int (plan.head_maxp_info.xMin);
1713
0
      encoder.encode_int (plan.head_maxp_info.yMin);
1714
0
      encoder.encode_int (plan.head_maxp_info.xMax);
1715
0
      encoder.encode_int (plan.head_maxp_info.yMax);
1716
0
      encoder.encode_op (OpCode_FontBBox);
1717
1718
0
      if (encoder.in_error () || !c->embed (bbox_buff.as_bytes ().arrayZ, bbox_buff.length))
1719
0
      {
1720
0
        c->pop_discard ();
1721
0
        return_trace (false);
1722
0
      }
1723
0
    }
1724
1725
    // Serialize charset operator
1726
0
    if (charset_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_charset, charset_link, whence_t::Absolute)))
1727
0
    {
1728
0
      c->pop_discard ();
1729
0
      return_trace (false);
1730
0
    }
1731
1732
    // Serialize FDSelect operator (required for CID-keyed CFF1 fonts)
1733
0
    if (plan.info.fd_select.link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDSelect, plan.info.fd_select.link, whence_t::Absolute)))
1734
0
    {
1735
0
      c->pop_discard ();
1736
0
      return_trace (false);
1737
0
    }
1738
1739
    // Serialize FDArray operator (required for CID-keyed CFF1 fonts)
1740
0
    if (plan.info.fd_array_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDArray, plan.info.fd_array_link, whence_t::Absolute)))
1741
0
    {
1742
0
      c->pop_discard ();
1743
0
      return_trace (false);
1744
0
    }
1745
1746
    // Serialize other operators from CFF2 TopDict
1747
0
    for (const auto &opstr : cff2_topDict.values)
1748
0
    {
1749
0
      if (unlikely (!topSzr.serialize (c, opstr, plan.info)))
1750
0
      {
1751
0
        c->pop_discard ();
1752
0
        return_trace (false);
1753
0
      }
1754
0
    }
1755
1756
0
    unsigned top_size = c->length ();
1757
0
    c->pop_pack (false);
1758
1759
    // Serialize INDEX header
1760
0
    auto *dest = c->start_embed<CFF1Index> ();
1761
0
    if (unlikely (!dest->serialize_header (c, hb_iter (&top_size, 1), top_size)))
1762
0
      return_trace (false);
1763
0
  }
1764
1765
0
  return_trace (true);
1766
0
}
1767
1768
} /* namespace CFF */
1769
1770
static bool _serialize_cff2_charstrings (hb_serialize_context_t *c,
1771
           cff2_subset_plan &plan,
1772
           const OT::cff2::accelerator_subset_t  &acc)
1773
0
{
1774
0
  c->push ();
1775
1776
0
  unsigned data_size = 0;
1777
0
  unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
1778
0
  if (unlikely (!c->start_zerocopy (total_size)))
1779
0
    return false;
1780
1781
0
  auto *cs = c->start_embed<CFF2CharStrings> ();
1782
0
  if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size)))
1783
0
  {
1784
0
    c->pop_discard ();
1785
0
    return false;
1786
0
  }
1787
1788
0
  plan.info.char_strings_link = c->pop_pack (false);
1789
0
  return true;
1790
0
}
1791
1792
bool
1793
OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
1794
             struct cff2_subset_plan &plan,
1795
             hb_array_t<int> normalized_coords) const
1796
0
{
1797
  /* push charstrings onto the object stack first which will ensure it packs as the last
1798
     object in the table. Keeping the chastrings last satisfies the requirements for patching
1799
     via IFTB. If this ordering needs to be changed in the future, charstrings should be left
1800
     at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
1801
0
  if (!_serialize_cff2_charstrings(c, plan, *this))
1802
0
    return false;
1803
1804
  /* private dicts & local subrs */
1805
0
  hb_vector_t<table_info_t>  private_dict_infos;
1806
0
  if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
1807
1808
0
  for (int i = (int)privateDicts.length; --i >= 0 ;)
1809
0
  {
1810
0
    if (plan.fdmap.has (i))
1811
0
    {
1812
0
      objidx_t  subrs_link = 0;
1813
1814
0
      if (plan.subset_localsubrs[i].length > 0)
1815
0
      {
1816
0
  auto *dest = c->push <CFF2Subrs> ();
1817
0
  if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
1818
0
    subrs_link = c->pop_pack (false);
1819
0
  else
1820
0
  {
1821
0
    c->pop_discard ();
1822
0
    return false;
1823
0
  }
1824
0
      }
1825
0
      auto *pd = c->push<PrivateDict> ();
1826
0
      cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
1827
0
             varStore, normalized_coords,
1828
0
             plan.partial_instancing ? &plan.instancing : nullptr);
1829
0
      if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
1830
0
      {
1831
0
  unsigned fd = plan.fdmap[i];
1832
0
  private_dict_infos[fd].size = c->length ();
1833
0
  private_dict_infos[fd].link = c->pop_pack ();
1834
0
      }
1835
0
      else
1836
0
      {
1837
0
  c->pop_discard ();
1838
0
  return false;
1839
0
      }
1840
0
    }
1841
0
  }
1842
1843
  /* FDSelect */
1844
0
  if (fdSelect != &Null (CFF2FDSelect))
1845
0
  {
1846
0
    c->push ();
1847
0
    if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
1848
0
             plan.orig_fdcount,
1849
0
             plan.subset_fdselect_format, plan.subset_fdselect_size,
1850
0
             plan.subset_fdselect_ranges)))
1851
0
      plan.info.fd_select.link = c->pop_pack ();
1852
0
    else
1853
0
    {
1854
0
      c->pop_discard ();
1855
0
      return false;
1856
0
    }
1857
0
  }
1858
1859
  /* FDArray (FD Index) */
1860
0
  {
1861
0
    auto *fda = c->push<CFF2FDArray> ();
1862
0
    cff_font_dict_op_serializer_t fontSzr;
1863
0
    auto it =
1864
0
    + hb_zip (+ hb_iter (fontDicts)
1865
0
        | hb_filter ([&] (const cff2_font_dict_values_t &_)
1866
0
    { return plan.fdmap.has (&_ - &fontDicts[0]); }),
1867
0
        hb_iter (private_dict_infos))
1868
0
    ;
1869
0
    if (unlikely (!fda->serialize (c, it, fontSzr)))
1870
0
    {
1871
0
      c->pop_discard ();
1872
0
      return false;
1873
0
    }
1874
0
    plan.info.fd_array_link = c->pop_pack (false);
1875
0
  }
1876
1877
  /* variation store */
1878
0
  if (varStore != &Null (CFF2ItemVariationStore) &&
1879
0
      !plan.pinned)
1880
0
  {
1881
0
    if (plan.partial_instancing)
1882
0
    {
1883
0
      if (plan.instancing.has_variations ())
1884
0
      {
1885
0
  c->push ();
1886
0
  if (unlikely (!plan.instancing.serialize_var_store (c, *plan.axis_tags)))
1887
0
  {
1888
0
    c->pop_discard ();
1889
0
    return false;
1890
0
  }
1891
0
  plan.info.var_store_link = c->pop_pack (false);
1892
0
      }
1893
      /* else: the store emptied; the vstore op gets dropped along with it. */
1894
0
    }
1895
0
    else
1896
0
    {
1897
0
      auto *dest = c->push<CFF2ItemVariationStore> ();
1898
0
      if (unlikely (!dest->serialize (c, varStore)))
1899
0
      {
1900
0
  c->pop_discard ();
1901
0
  return false;
1902
0
      }
1903
0
      plan.info.var_store_link = c->pop_pack (false);
1904
0
    }
1905
0
  }
1906
1907
0
  OT::cff2 *cff2 = c->allocate_min<OT::cff2> ();
1908
0
  if (unlikely (!cff2)) return false;
1909
1910
  /* header */
1911
0
  cff2->version.major = 0x02;
1912
0
  cff2->version.minor = 0x00;
1913
0
  cff2->topDict = OT::cff2::static_size;
1914
1915
  /* top dict */
1916
0
  {
1917
0
    TopDict &dict = cff2 + cff2->topDict;
1918
0
    cff2_top_dict_op_serializer_t topSzr;
1919
0
    if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false;
1920
0
    cff2->topDictSize = c->head - (const char *)&dict;
1921
0
  }
1922
1923
  /* global subrs */
1924
0
  {
1925
0
    auto *dest = c->start_embed <CFF2Subrs> ();
1926
0
    return dest->serialize (c, plan.subset_globalsubrs);
1927
0
  }
1928
0
}
1929
1930
bool
1931
OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const
1932
0
{
1933
0
  cff2_subset_plan cff2_plan;
1934
1935
0
  if (unlikely (!cff2_plan.create (*this, c->plan))) return false;
1936
1937
  // If instantiating (pinned) and downgrade flag is set, convert to CFF1
1938
0
  if (cff2_plan.pinned && (c->plan->flags & HB_SUBSET_FLAGS_DOWNGRADE_CFF2))
1939
0
  {
1940
    // Serialize CFF1 to the subsetter's serializer
1941
    // If we run out of room, returning true will cause subsetter to retry with larger buffer
1942
0
    bool result = CFF::serialize_cff2_to_cff1 (c->serializer, cff2_plan, topDict, *this);
1943
1944
0
    if (c->serializer->ran_out_of_room ())
1945
0
      return true; // Subsetter will retry with larger buffer
1946
1947
0
    if (result && !c->serializer->in_error ())
1948
0
    {
1949
      // Success - end serialization to resolve links
1950
0
      c->serializer->end_serialize ();
1951
1952
      // Copy the serialized CFF1 data and add as CFF table
1953
0
      hb_blob_t *cff_blob = c->serializer->copy_blob ();
1954
0
      if (cff_blob)
1955
0
      {
1956
0
        c->plan->add_table (HB_TAG('C','F','F',' '), cff_blob);
1957
0
        hb_blob_destroy (cff_blob);
1958
1959
        // Return false to signal CFF2 table is not needed
1960
0
        return false;
1961
0
      }
1962
0
    }
1963
1964
    // Conversion failed - don't fall back, fail hard for debugging
1965
0
    return false;
1966
0
  }
1967
1968
0
  return serialize (c->serializer, cff2_plan,
1969
0
        c->plan->normalized_coords.as_array ());
1970
0
}
1971
1972
#endif