Coverage Report

Created: 2025-07-01 07:01

/src/harfbuzz/src/hb-ot-cff2-table.hh
Line
Count
Source (jump to first uncovered line)
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
#ifndef HB_OT_CFF2_TABLE_HH
28
#define HB_OT_CFF2_TABLE_HH
29
30
#include "hb-ot-cff-common.hh"
31
#include "hb-subset-cff-common.hh"
32
#include "hb-draw.hh"
33
#include "hb-paint.hh"
34
35
namespace CFF {
36
37
/*
38
 * CFF2 -- Compact Font Format (CFF) Version 2
39
 * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
40
 */
41
#define HB_OT_TAG_CFF2 HB_TAG('C','F','F','2')
42
43
typedef CFF2Index         CFF2CharStrings;
44
typedef Subrs<HBUINT32>   CFF2Subrs;
45
46
typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
47
typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
48
49
struct CFF2FDSelect
50
{
51
  bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
52
0
  {
53
0
    TRACE_SERIALIZE (this);
54
0
    unsigned int size = src.get_size (num_glyphs);
55
0
    CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
56
0
    if (unlikely (!dest)) return_trace (false);
57
0
    hb_memcpy (dest, &src, size);
58
0
    return_trace (true);
59
0
  }
60
61
  unsigned int get_size (unsigned int num_glyphs) const
62
0
  {
63
0
    switch (format)
64
0
    {
65
0
    case 0: hb_barrier (); return format.static_size + u.format0.get_size (num_glyphs);
66
0
    case 3: hb_barrier (); return format.static_size + u.format3.get_size ();
67
0
    case 4: hb_barrier (); return format.static_size + u.format4.get_size ();
68
0
    default:return 0;
69
0
    }
70
0
  }
71
72
  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
73
0
  {
74
0
    if (this == &Null (CFF2FDSelect))
75
0
      return 0;
76
77
0
    switch (format)
78
0
    {
79
0
    case 0: hb_barrier (); return u.format0.get_fd (glyph);
80
0
    case 3: hb_barrier (); return u.format3.get_fd (glyph);
81
0
    case 4: hb_barrier (); return u.format4.get_fd (glyph);
82
0
    default:return 0;
83
0
    }
84
0
  }
85
86
  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
87
0
  {
88
0
    TRACE_SANITIZE (this);
89
0
    if (unlikely (!c->check_struct (this)))
90
0
      return_trace (false);
91
0
    hb_barrier ();
92
93
0
    switch (format)
94
0
    {
95
0
    case 0: hb_barrier (); return_trace (u.format0.sanitize (c, fdcount));
96
0
    case 3: hb_barrier (); return_trace (u.format3.sanitize (c, fdcount));
97
0
    case 4: hb_barrier (); return_trace (u.format4.sanitize (c, fdcount));
98
0
    default:return_trace (false);
99
0
    }
100
0
  }
101
102
  HBUINT8 format;
103
  union {
104
  FDSelect0 format0;
105
  FDSelect3 format3;
106
  FDSelect4 format4;
107
  } u;
108
  public:
109
  DEFINE_SIZE_MIN (2);
110
};
111
112
struct CFF2ItemVariationStore
113
{
114
  bool sanitize (hb_sanitize_context_t *c) const
115
0
  {
116
0
    TRACE_SANITIZE (this);
117
0
    return_trace (c->check_struct (this) &&
118
0
      hb_barrier () &&
119
0
      c->check_range (&varStore, size) &&
120
0
      varStore.sanitize (c));
121
0
  }
122
123
  bool serialize (hb_serialize_context_t *c, const CFF2ItemVariationStore *varStore)
124
0
  {
125
0
    TRACE_SERIALIZE (this);
126
0
    unsigned int size_ = varStore->get_size ();
127
0
    CFF2ItemVariationStore *dest = c->allocate_size<CFF2ItemVariationStore> (size_);
128
0
    if (unlikely (!dest)) return_trace (false);
129
0
    hb_memcpy (dest, varStore, size_);
130
0
    return_trace (true);
131
0
  }
132
133
0
  unsigned int get_size () const { return HBUINT16::static_size + size; }
134
135
  HBUINT16  size;
136
  ItemVariationStore  varStore;
137
138
  DEFINE_SIZE_MIN (2 + ItemVariationStore::min_size);
139
};
140
141
struct cff2_top_dict_values_t : top_dict_values_t<>
142
{
143
  void init ()
144
0
  {
145
0
    top_dict_values_t<>::init ();
146
0
    vstoreOffset = 0;
147
0
    FDSelectOffset = 0;
148
0
  }
149
0
  void fini () { top_dict_values_t<>::fini (); }
150
151
  int  vstoreOffset;
152
  int  FDSelectOffset;
153
};
154
155
struct cff2_top_dict_opset_t : top_dict_opset_t<>
156
{
157
  static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval)
158
0
  {
159
0
    switch (op) {
160
0
      case OpCode_FontMatrix:
161
0
  {
162
0
    dict_val_t val;
163
0
    val.init ();
164
0
    dictval.add_op (op, env.str_ref);
165
0
    env.clear_args ();
166
0
  }
167
0
  break;
168
169
0
      case OpCode_vstore:
170
0
  dictval.vstoreOffset = env.argStack.pop_int ();
171
0
  env.clear_args ();
172
0
  break;
173
0
      case OpCode_FDSelect:
174
0
  dictval.FDSelectOffset = env.argStack.pop_int ();
175
0
  env.clear_args ();
176
0
  break;
177
178
0
      default:
179
0
  SUPER::process_op (op, env, dictval);
180
  /* Record this operand below if stack is empty, otherwise done */
181
0
  if (!env.argStack.is_empty ()) return;
182
0
    }
183
184
0
    if (unlikely (env.in_error ())) return;
185
186
0
    dictval.add_op (op, env.str_ref);
187
0
  }
188
189
  typedef top_dict_opset_t<> SUPER;
190
};
191
192
struct cff2_font_dict_values_t : dict_values_t<op_str_t>
193
{
194
  void init ()
195
0
  {
196
0
    dict_values_t<op_str_t>::init ();
197
0
    privateDictInfo.init ();
198
0
  }
199
0
  void fini () { dict_values_t<op_str_t>::fini (); }
200
201
  table_info_t    privateDictInfo;
202
};
203
204
struct cff2_font_dict_opset_t : dict_opset_t
205
{
206
  static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval)
207
0
  {
208
0
    switch (op) {
209
0
      case OpCode_Private:
210
0
  dictval.privateDictInfo.offset = env.argStack.pop_uint ();
211
0
  dictval.privateDictInfo.size = env.argStack.pop_uint ();
212
0
  env.clear_args ();
213
0
  break;
214
215
0
      default:
216
0
  SUPER::process_op (op, env);
217
0
  if (!env.argStack.is_empty ())
218
0
    return;
219
0
    }
220
221
0
    if (unlikely (env.in_error ())) return;
222
223
0
    dictval.add_op (op, env.str_ref);
224
0
  }
225
226
  private:
227
  typedef dict_opset_t SUPER;
228
};
229
230
template <typename VAL>
231
struct cff2_private_dict_values_base_t : dict_values_t<VAL>
232
{
233
  void init ()
234
0
  {
235
0
    dict_values_t<VAL>::init ();
236
0
    subrsOffset = 0;
237
0
    localSubrs = &Null (CFF2Subrs);
238
0
    ivs = 0;
239
0
  }
Unexecuted instantiation: CFF::cff2_private_dict_values_base_t<CFF::dict_val_t>::init()
Unexecuted instantiation: CFF::cff2_private_dict_values_base_t<CFF::op_str_t>::init()
240
  void fini () { dict_values_t<VAL>::fini (); }
241
242
  int                subrsOffset;
243
  const CFF2Subrs   *localSubrs;
244
  unsigned int      ivs;
245
};
246
247
typedef cff2_private_dict_values_base_t<op_str_t> cff2_private_dict_values_subset_t;
248
typedef cff2_private_dict_values_base_t<num_dict_val_t> cff2_private_dict_values_t;
249
250
struct cff2_priv_dict_interp_env_t : num_interp_env_t
251
{
252
  cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) :
253
0
    num_interp_env_t (str) {}
254
255
  void process_vsindex ()
256
0
  {
257
0
    if (likely (!seen_vsindex))
258
0
    {
259
0
      set_ivs (argStack.pop_uint ());
260
0
    }
261
0
    seen_vsindex = true;
262
0
  }
263
264
0
  unsigned int get_ivs () const { return ivs; }
265
0
  void   set_ivs (unsigned int ivs_) { ivs = ivs_; }
266
267
  protected:
268
  unsigned int  ivs = 0;
269
  bool    seen_vsindex = false;
270
};
271
272
struct cff2_private_dict_opset_t : dict_opset_t
273
{
274
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval)
275
0
  {
276
0
    num_dict_val_t val;
277
0
    val.init ();
278
279
0
    switch (op) {
280
0
      case OpCode_StdHW:
281
0
      case OpCode_StdVW:
282
0
      case OpCode_BlueScale:
283
0
      case OpCode_BlueShift:
284
0
      case OpCode_BlueFuzz:
285
0
      case OpCode_ExpansionFactor:
286
0
      case OpCode_LanguageGroup:
287
0
      case OpCode_BlueValues:
288
0
      case OpCode_OtherBlues:
289
0
      case OpCode_FamilyBlues:
290
0
      case OpCode_FamilyOtherBlues:
291
0
      case OpCode_StemSnapH:
292
0
      case OpCode_StemSnapV:
293
0
  env.clear_args ();
294
0
  break;
295
0
      case OpCode_Subrs:
296
0
  dictval.subrsOffset = env.argStack.pop_int ();
297
0
  env.clear_args ();
298
0
  break;
299
0
      case OpCode_vsindexdict:
300
0
  env.process_vsindex ();
301
0
  dictval.ivs = env.get_ivs ();
302
0
  env.clear_args ();
303
0
  break;
304
0
      case OpCode_blenddict:
305
0
  break;
306
307
0
      default:
308
0
  dict_opset_t::process_op (op, env);
309
0
  if (!env.argStack.is_empty ()) return;
310
0
  break;
311
0
    }
312
313
0
    if (unlikely (env.in_error ())) return;
314
315
0
    dictval.add_op (op, env.str_ref, val);
316
0
  }
317
};
318
319
struct cff2_private_dict_opset_subset_t : dict_opset_t
320
{
321
  static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval)
322
0
  {
323
0
    switch (op) {
324
0
      case OpCode_BlueValues:
325
0
      case OpCode_OtherBlues:
326
0
      case OpCode_FamilyBlues:
327
0
      case OpCode_FamilyOtherBlues:
328
0
      case OpCode_StdHW:
329
0
      case OpCode_StdVW:
330
0
      case OpCode_BlueScale:
331
0
      case OpCode_BlueShift:
332
0
      case OpCode_BlueFuzz:
333
0
      case OpCode_StemSnapH:
334
0
      case OpCode_StemSnapV:
335
0
      case OpCode_LanguageGroup:
336
0
      case OpCode_ExpansionFactor:
337
0
  env.clear_args ();
338
0
  break;
339
0
340
0
      case OpCode_blenddict:
341
0
  env.clear_args ();
342
0
  return;
343
0
344
0
      case OpCode_Subrs:
345
0
  dictval.subrsOffset = env.argStack.pop_int ();
346
0
  env.clear_args ();
347
0
  break;
348
0
349
0
      default:
350
0
  SUPER::process_op (op, env);
351
0
  if (!env.argStack.is_empty ()) return;
352
0
  break;
353
0
    }
354
0
355
0
    if (unlikely (env.in_error ())) return;
356
0
357
0
    dictval.add_op (op, env.str_ref);
358
0
  }
359
360
  private:
361
  typedef dict_opset_t SUPER;
362
};
363
364
typedef dict_interpreter_t<cff2_top_dict_opset_t, cff2_top_dict_values_t> cff2_top_dict_interpreter_t;
365
typedef dict_interpreter_t<cff2_font_dict_opset_t, cff2_font_dict_values_t> cff2_font_dict_interpreter_t;
366
367
struct CFF2FDArray : FDArray<HBUINT32>
368
{
369
  /* FDArray::serialize does not compile without this partial specialization */
370
  template <typename ITER, typename OP_SERIALIZER>
371
  bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr)
372
  { return FDArray<HBUINT32>::serialize<cff2_font_dict_values_t, table_info_t> (c, it, opszr); }
373
};
374
375
} /* namespace CFF */
376
377
namespace OT {
378
379
using namespace CFF;
380
381
struct cff2
382
{
383
  static constexpr hb_tag_t tableTag = HB_OT_TAG_CFF2;
384
385
  bool sanitize (hb_sanitize_context_t *c) const
386
0
  {
387
0
    TRACE_SANITIZE (this);
388
0
    return_trace (c->check_struct (this) &&
389
0
      hb_barrier () &&
390
0
      likely (version.major == 2));
391
0
  }
392
393
  template <typename PRIVOPSET, typename PRIVDICTVAL>
394
  struct accelerator_templ_t
395
  {
396
    static constexpr hb_tag_t tableTag = cff2::tableTag;
397
398
    accelerator_templ_t (hb_face_t *face)
399
0
    {
400
0
      if (!face) return;
401
402
0
      topDict.init ();
403
0
      fontDicts.init ();
404
0
      privateDicts.init ();
405
406
0
      this->blob = sc.reference_table<cff2> (face);
407
408
      /* setup for run-time sanitization */
409
0
      sc.init (this->blob);
410
0
      sc.start_processing ();
411
412
0
      const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
413
414
0
      if (cff2 == &Null (OT::cff2))
415
0
        goto fail;
416
417
0
      { /* parse top dict */
418
0
  hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize);
419
0
  if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
420
0
  hb_barrier ();
421
0
  num_interp_env_t env (topDictStr);
422
0
  cff2_top_dict_interpreter_t top_interp (env);
423
0
  topDict.init ();
424
0
  if (unlikely (!top_interp.interpret (topDict))) goto fail;
425
0
      }
426
427
0
      globalSubrs = &StructAtOffsetOrNull<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize, sc);
428
0
      varStore = &StructAtOffsetOrNull<CFF2ItemVariationStore> (cff2, topDict.vstoreOffset, sc);
429
0
      charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset, sc);
430
0
      fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset, sc);
431
0
      fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset, sc, fdArray->count);
432
433
0
      if (charStrings == &Null (CFF2CharStrings) ||
434
0
    globalSubrs == &Null (CFF2Subrs) ||
435
0
    fdArray == &Null (CFF2FDArray))
436
0
        goto fail;
437
438
0
      num_glyphs = charStrings->count;
439
0
      if (num_glyphs != sc.get_num_glyphs ())
440
0
        goto fail;
441
442
0
      fdCount = fdArray->count;
443
0
      if (!privateDicts.resize (fdCount))
444
0
        goto fail;
445
446
      /* parse font dicts and gather private dicts */
447
0
      for (unsigned int i = 0; i < fdCount; i++)
448
0
      {
449
0
  const hb_ubytes_t fontDictStr = (*fdArray)[i];
450
0
  if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
451
0
  hb_barrier ();
452
0
  cff2_font_dict_values_t  *font;
453
0
  num_interp_env_t env (fontDictStr);
454
0
  cff2_font_dict_interpreter_t font_interp (env);
455
0
  font = fontDicts.push ();
456
0
  if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail;
457
0
  font->init ();
458
0
  if (unlikely (!font_interp.interpret (*font))) goto fail;
459
460
0
  const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size);
461
0
  if (unlikely (font->privateDictInfo.size &&
462
0
          privDictStr == (const unsigned char *) &Null (UnsizedByteStr))) goto fail;
463
0
  cff2_priv_dict_interp_env_t env2 (privDictStr);
464
0
  dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2);
465
0
  privateDicts[i].init ();
466
0
  if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail;
467
468
0
  privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset, sc);
469
0
      }
470
471
0
      return;
472
473
0
      fail:
474
0
        _fini ();
475
0
    }
476
0
    ~accelerator_templ_t () { _fini (); }
477
    void _fini ()
478
0
    {
479
0
      sc.end_processing ();
480
0
      topDict.fini ();
481
0
      fontDicts.fini ();
482
0
      privateDicts.fini ();
483
0
      hb_blob_destroy (blob);
484
0
      blob = nullptr;
485
486
0
      auto *scalars = cached_scalars_vector.get_acquire ();
487
0
      if (scalars && cached_scalars_vector.cmpexch (scalars, nullptr))
488
0
      {
489
0
  scalars->fini ();
490
0
  hb_free (scalars);
491
0
      }
492
0
    }
Unexecuted instantiation: OT::cff2::accelerator_templ_t<CFF::cff2_private_dict_opset_t, CFF::cff2_private_dict_values_base_t<CFF::dict_val_t> >::_fini()
Unexecuted instantiation: OT::cff2::accelerator_templ_t<CFF::cff2_private_dict_opset_subset_t, CFF::cff2_private_dict_values_base_t<CFF::op_str_t> >::_fini()
493
494
    hb_vector_t<uint16_t> *create_glyph_to_sid_map () const
495
    {
496
      return nullptr;
497
    }
498
499
    hb_blob_t *get_blob () const { return blob; }
500
501
0
    bool is_valid () const { return blob; }
502
503
    protected:
504
    hb_sanitize_context_t sc;
505
506
    public:
507
    hb_blob_t     *blob = nullptr;
508
    cff2_top_dict_values_t  topDict;
509
    const CFF2Subrs   *globalSubrs = nullptr;
510
    const CFF2ItemVariationStore  *varStore = nullptr;
511
    const CFF2CharStrings *charStrings = nullptr;
512
    const CFF2FDArray   *fdArray = nullptr;
513
    const CFF2FDSelect    *fdSelect = nullptr;
514
    unsigned int    fdCount = 0;
515
516
    hb_vector_t<cff2_font_dict_values_t>     fontDicts;
517
    hb_vector_t<PRIVDICTVAL>  privateDicts;
518
519
    mutable hb_atomic_t<hb_vector_t<float> *> cached_scalars_vector;
520
521
    unsigned int        num_glyphs = 0;
522
  };
523
524
  struct accelerator_t : accelerator_templ_t<cff2_private_dict_opset_t, cff2_private_dict_values_t>
525
  {
526
0
    accelerator_t (hb_face_t *face) : accelerator_templ_t (face) {}
527
528
    HB_INTERNAL bool get_extents (hb_font_t *font,
529
          hb_codepoint_t glyph,
530
          hb_glyph_extents_t *extents) const;
531
    HB_INTERNAL bool get_extents_at (hb_font_t *font,
532
             hb_codepoint_t glyph,
533
             hb_glyph_extents_t *extents,
534
             hb_array_t<const int> coords) const;
535
    HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
536
    HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
537
  };
538
539
  struct accelerator_subset_t : accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t>
540
  {
541
0
    accelerator_subset_t (hb_face_t *face) : SUPER (face) {}
542
    ~accelerator_subset_t ()
543
0
    {
544
0
      if (cff_accelerator)
545
0
  cff_subset_accelerator_t::destroy (cff_accelerator);
546
0
    }
547
548
    HB_INTERNAL bool subset (hb_subset_context_t *c) const;
549
    HB_INTERNAL bool serialize (hb_serialize_context_t *c,
550
        struct cff2_subset_plan &plan,
551
        hb_array_t<int> normalized_coords) const;
552
553
    mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr;
554
555
    typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> SUPER;
556
  };
557
558
  public:
559
  FixedVersion<HBUINT8>   version;  /* Version of CFF2 table. set to 0x0200u */
560
  NNOffsetTo<TopDict, HBUINT8>  topDict;  /* headerSize = Offset to Top DICT. */
561
  HBUINT16      topDictSize;  /* Top DICT size */
562
563
  public:
564
  DEFINE_SIZE_STATIC (5);
565
};
566
567
struct cff2_accelerator_t : cff2::accelerator_t {
568
0
  cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {}
569
};
570
571
struct cff2_subset_accelerator_t : cff2::accelerator_subset_t {
572
0
  cff2_subset_accelerator_t (hb_face_t *face) : cff2::accelerator_subset_t (face) {}
573
};
574
575
} /* namespace OT */
576
577
#endif /* HB_OT_CFF2_TABLE_HH */