Coverage Report

Created: 2026-10-30 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
2
#define OT_LAYOUT_GPOS_VALUEFORMAT_HH
3
4
#include "../../../hb-ot-layout-gsubgpos.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GPOS_impl {
9
10
typedef HBUINT16 Value;
11
12
struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases.
13
14
typedef UnsizedArrayOf<Value> ValueRecord;
15
16
struct ValueFormat : HBUINT16
17
{
18
  enum Flags {
19
    xPlacement  = 0x0001u,      /* Includes horizontal adjustment for placement */
20
    yPlacement  = 0x0002u,      /* Includes vertical adjustment for placement */
21
    xAdvance    = 0x0004u,      /* Includes horizontal adjustment for advance */
22
    yAdvance    = 0x0008u,      /* Includes vertical adjustment for advance */
23
    xPlaDevice  = 0x0010u,      /* Includes horizontal Device table for placement */
24
    yPlaDevice  = 0x0020u,      /* Includes vertical Device table for placement */
25
    xAdvDevice  = 0x0040u,      /* Includes horizontal Device table for advance */
26
    yAdvDevice  = 0x0080u,      /* Includes vertical Device table for advance */
27
    ignored     = 0x0F00u,      /* Was used in TrueType Open for MM fonts */
28
    reserved    = 0xF000u,      /* For future use */
29
30
    devices     = 0x00F0u       /* Mask for having any Device table */
31
  };
32
33
/* All fields are options.  Only those available advance the value pointer. */
34
#if 0
35
  HBINT16               xPlacement;     /* Horizontal adjustment for
36
                                         * placement--in design units */
37
  HBINT16               yPlacement;     /* Vertical adjustment for
38
                                         * placement--in design units */
39
  HBINT16               xAdvance;       /* Horizontal adjustment for
40
                                         * advance--in design units (only used
41
                                         * for horizontal writing) */
42
  HBINT16               yAdvance;       /* Vertical adjustment for advance--in
43
                                         * design units (only used for vertical
44
                                         * writing) */
45
  Offset16To<Device>    xPlaDevice;     /* Offset to Device table for
46
                                         * horizontal placement--measured from
47
                                         * beginning of PosTable (may be NULL) */
48
  Offset16To<Device>    yPlaDevice;     /* Offset to Device table for vertical
49
                                         * placement--measured from beginning
50
                                         * of PosTable (may be NULL) */
51
  Offset16To<Device>    xAdvDevice;     /* Offset to Device table for
52
                                         * horizontal advance--measured from
53
                                         * beginning of PosTable (may be NULL) */
54
  Offset16To<Device>    yAdvDevice;     /* Offset to Device table for vertical
55
                                         * advance--measured from beginning of
56
                                         * PosTable (may be NULL) */
57
#endif
58
59
0
  NumType& operator = (uint16_t i) { v = i; return *this; }
60
61
  // Note: spec says skip 2 bytes per bit in the valueformat. But reports
62
  // from Microsoft developers indicate that only the fields that are
63
  // currently defined are counted. We don't expect any new fields to
64
  // be added to ValueFormat. As such, we use the faster hb_popcount8
65
  // that only processes the lowest 8 bits.
66
58
  unsigned int get_len () const  { return hb_popcount8 ((uint8_t) *this); }
67
0
  size_t get_size () const { return get_len () * Value::static_size; }
68
69
0
  hb_vector_t<unsigned> get_device_table_indices () const {
70
0
    unsigned i = 0;
71
0
    hb_vector_t<unsigned> result;
72
0
    unsigned format = *this;
73
0
74
0
    if (format & xPlacement) i++;
75
0
    if (format & yPlacement) i++;
76
0
    if (format & xAdvance)   i++;
77
0
    if (format & yAdvance)   i++;
78
0
79
0
    if (format & xPlaDevice) result.push (i++);
80
0
    if (format & yPlaDevice) result.push (i++);
81
0
    if (format & xAdvDevice) result.push (i++);
82
0
    if (format & yAdvDevice) result.push (i++);
83
0
84
0
    return result;
85
0
  }
86
87
  bool apply_value (hb_ot_apply_context_t *c,
88
                    const ValueBase       *base,
89
                    const Value           *values,
90
                    hb_glyph_position_t   &glyph_pos) const
91
0
  {
92
0
    bool ret = false;
93
0
    unsigned int format = *this;
94
0
    if (!format) return ret;
95
96
0
    hb_font_t *font = c->font;
97
0
    bool horizontal =
98
0
#ifndef HB_NO_VERTICAL
99
0
      HB_DIRECTION_IS_HORIZONTAL (c->direction)
100
#else
101
      true
102
#endif
103
0
      ;
104
105
0
    if (format & xPlacement)
106
0
      glyph_pos.x_offset = hb_saturate_add (glyph_pos.x_offset, font->em_scale_x (get_short (values++, &ret)));
107
0
    if (format & yPlacement)
108
0
      glyph_pos.y_offset = hb_saturate_add (glyph_pos.y_offset, font->em_scale_y (get_short (values++, &ret)));
109
0
    if (format & xAdvance) {
110
0
      if (likely (horizontal))
111
0
  glyph_pos.x_advance = hb_saturate_add (glyph_pos.x_advance, font->em_scale_x (get_short (values, &ret)));
112
0
      values++;
113
0
    }
114
    /* y_advance values grow downward but font-space grows upward, hence negation */
115
0
    if (format & yAdvance) {
116
0
      if (unlikely (!horizontal))
117
0
  glyph_pos.y_advance = hb_saturate_sub (glyph_pos.y_advance, font->em_scale_y (get_short (values, &ret)));
118
0
      values++;
119
0
    }
120
121
0
    if (!has_device ()) return ret;
122
123
0
    bool use_x_device = font->x_ppem || font->has_nonzero_coords;
124
0
    bool use_y_device = font->y_ppem || font->has_nonzero_coords;
125
126
0
    if (!use_x_device && !use_y_device) return ret;
127
128
0
    const ItemVariationStore &store = c->var_store;
129
0
    auto *cache = c->var_store_cache;
130
131
    /* pixel -> fractional pixel */
132
0
    if (format & xPlaDevice)
133
0
    {
134
0
      if (use_x_device)
135
0
  glyph_pos.x_offset = hb_saturate_add (glyph_pos.x_offset, get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache));
136
0
      values++;
137
0
    }
138
0
    if (format & yPlaDevice)
139
0
    {
140
0
      if (use_y_device)
141
0
  glyph_pos.y_offset = hb_saturate_add (glyph_pos.y_offset, get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache));
142
0
      values++;
143
0
    }
144
0
    if (format & xAdvDevice)
145
0
    {
146
0
      if (horizontal && use_x_device)
147
0
  glyph_pos.x_advance = hb_saturate_add (glyph_pos.x_advance, get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache));
148
0
      values++;
149
0
    }
150
0
    if (format & yAdvDevice)
151
0
    {
152
      /* y_advance values grow downward but font-space grows upward, hence negation */
153
0
      if (!horizontal && use_y_device)
154
0
  glyph_pos.y_advance = hb_saturate_sub (glyph_pos.y_advance, get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache));
155
0
      values++;
156
0
    }
157
0
    return ret;
158
0
  }
159
160
  void position_single (hb_font_t *font,
161
                        hb_blob_t *table_blob,
162
                        hb_direction_t direction,
163
                        const ValueBase *base,
164
                        const Value *values,
165
                        hb_glyph_position_t &pos) const
166
0
  {
167
    /* This is ugly... */
168
0
    hb_buffer_t buffer {};
169
0
    buffer.props.direction = direction;
170
0
    hb_ot_apply_context_t c (1, font, &buffer, table_blob);
171
172
0
    apply_value (&c, base, values, pos);
173
0
  }
174
175
  unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
176
                                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
177
0
  {
178
0
    unsigned int format = *this;
179
0
    for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
180
0
      if (format & flag)
181
0
      {
182
0
        if (strip_hints && flag >= xPlaDevice)
183
0
        {
184
0
          format = format & ~flag;
185
0
          values++;
186
0
          continue;
187
0
        }
188
0
        if (varidx_delta_map && flag >= xPlaDevice)
189
0
        {
190
0
          update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
191
0
          continue;
192
0
        }
193
0
        /* do not strip empty when instancing, cause we don't know whether the new
194
0
         * default value is 0 or not */
195
0
        if (strip_empty) should_drop (*values, (Flags) flag, &format);
196
0
        values++;
197
0
      }
198
0
    }
199
0
200
0
    return format;
201
0
  }
202
203
  template<typename Iterator,
204
      hb_requires (hb_is_iterator (Iterator))>
205
  unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
206
0
                                     const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const {
207
0
    unsigned int new_format = 0;
208
0
209
0
    for (const hb_array_t<const Value>& values : it)
210
0
      new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
211
0
212
0
    return new_format;
213
0
  }
Unexecuted instantiation: hb-ot-face.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_7LPv0EENS1_25single_pos_value_mapper_tEL24hb_function_sortedness_t1ELSJ_0EERK3$_6LSM_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NST_6item_tEEE5valueEvE4typeELSJ_0EEEjST_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIj9hb_pair_tIjiELb0EE
Unexecuted instantiation: hb-aat-layout.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_7LPv0EENS1_25single_pos_value_mapper_tEL24hb_function_sortedness_t1ELSJ_0EERK3$_6LSM_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NST_6item_tEEE5valueEvE4typeELSJ_0EEEjST_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIj9hb_pair_tIjiELb0EE
Unexecuted instantiation: hb-ot-layout.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_7LPv0EENS1_25single_pos_value_mapper_tEL24hb_function_sortedness_t1ELSJ_0EERK3$_6LSM_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NST_6item_tEEE5valueEvE4typeELSJ_0EEEjST_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIj9hb_pair_tIjiELb0EE
Unexecuted instantiation: hb-ot-shape-fallback.cc:_ZNK2OT6Layout9GPOS_impl11ValueFormat20get_effective_formatI13hb_map_iter_tIS4_I16hb_filter_iter_tI13hb_zip_iter_tINS0_6Common8Coverage6iter_tE15hb_range_iter_tIjjEERK8hb_set_tRK3$_7LPv0EENS1_25single_pos_value_mapper_tEL24hb_function_sortedness_t1ELSJ_0EERK3$_6LSM_0ELSJ_0EETnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT_NST_6item_tEEE5valueEvE4typeELSJ_0EEEjST_bbPKNS1_9ValueBaseEPK12hb_hashmap_tIj9hb_pair_tIjiELb0EE
214
215
  void copy_values (hb_serialize_context_t *c,
216
                    unsigned int new_format,
217
                    const ValueBase *base,
218
                    const Value *values,
219
                    const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
220
0
  {
221
0
    unsigned int format = *this;
222
0
    if (!format) return;
223
0
224
0
    HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
225
0
    if ((format | new_format) & xPlacement)
226
0
      x_placement = copy_value (c, new_format, xPlacement, format & xPlacement ? *values++ : Null (Value));
227
0
    if ((format | new_format) & yPlacement)
228
0
      y_placement = copy_value (c, new_format, yPlacement, format & yPlacement ? *values++ : Null (Value));
229
0
    if ((format | new_format) & xAdvance)
230
0
      x_adv = copy_value (c, new_format, xAdvance, format & xAdvance ? *values++ : Null (Value));
231
0
    if ((format | new_format) & yAdvance)
232
0
      y_adv = copy_value (c, new_format, yAdvance, format & yAdvance ? *values++ : Null (Value));
233
0
234
0
    if (!has_device ())
235
0
      return;
236
0
237
0
    if (format & xPlaDevice)
238
0
    {
239
0
      add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
240
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
241
0
    }
242
0
243
0
    if (format & yPlaDevice)
244
0
    {
245
0
      add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
246
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
247
0
    }
248
0
249
0
    if (format & xAdvDevice)
250
0
    {
251
0
      add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
252
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
253
0
    }
254
0
255
0
    if (format & yAdvDevice)
256
0
    {
257
0
      add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
258
0
      copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
259
0
    }
260
0
  }
261
262
  HBINT16* copy_value (hb_serialize_context_t *c,
263
                       unsigned int new_format,
264
                       Flags flag,
265
                       Value value) const
266
0
  {
267
0
    // Filter by new format.
268
0
    if (!(new_format & flag)) return nullptr;
269
0
    return reinterpret_cast<HBINT16 *> (c->copy (value));
270
0
  }
271
272
  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
273
                                  const ValueBase *base,
274
                                  const hb_array_t<const Value>& values) const
275
0
  {
276
0
    unsigned format = *this;
277
0
    unsigned i = 0;
278
0
    if (format & xPlacement) i++;
279
0
    if (format & yPlacement) i++;
280
0
    if (format & xAdvance) i++;
281
0
    if (format & yAdvance) i++;
282
0
    if (format & xPlaDevice)
283
0
    {
284
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
285
0
      i++;
286
0
    }
287
0
288
0
    if (format & ValueFormat::yPlaDevice)
289
0
    {
290
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
291
0
      i++;
292
0
    }
293
0
294
0
    if (format & ValueFormat::xAdvDevice)
295
0
    {
296
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
297
0
      i++;
298
0
    }
299
0
300
0
    if (format & ValueFormat::yAdvDevice)
301
0
    {
302
0
      (base + get_device (&(values[i]))).collect_variation_indices (c);
303
0
      i++;
304
0
    }
305
0
  }
306
307
  private:
308
  bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
309
0
  {
310
0
    unsigned int format = *this;
311
312
0
    if (format & xPlacement) values++;
313
0
    if (format & yPlacement) values++;
314
0
    if (format & xAdvance)   values++;
315
0
    if (format & yAdvance)   values++;
316
317
0
    if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
318
0
    if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
319
0
    if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
320
0
    if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
321
322
0
    return true;
323
0
  }
324
325
  static inline Offset16To<Device, ValueBase>& get_device (Value* value)
326
0
  {
327
0
    return *static_cast<Offset16To<Device, ValueBase> *> (value);
328
0
  }
329
  static inline const Offset16To<Device, ValueBase>& get_device (const Value* value)
330
0
  {
331
0
    return *static_cast<const Offset16To<Device, ValueBase> *> (value);
332
0
  }
333
  static inline const Device& get_device (const Value* value,
334
            bool *worked,
335
            const ValueBase *base,
336
            hb_sanitize_context_t &c)
337
0
  {
338
0
    if (worked) *worked |= bool (*value);
339
0
    auto &offset = *static_cast<const Offset16To<Device> *> (value);
340
341
0
    if (unlikely (!offset.sanitize (&c, base)))
342
0
      return Null(Device);
343
0
    hb_barrier ();
344
345
0
    return base + offset;
346
0
  }
347
348
  void add_delta_to_value (HBINT16 *value,
349
                           const ValueBase *base,
350
                           const Value *src_value,
351
                           const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
352
0
  {
353
0
    if (!value) return;
354
0
    unsigned varidx = (base + get_device (src_value)).get_variation_index ();
355
0
    hb_pair_t<unsigned, int> *varidx_delta;
356
0
    if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
357
0
358
0
    *value += hb_second (*varidx_delta);
359
0
  }
360
361
  bool copy_device (hb_serialize_context_t *c,
362
                    const ValueBase *base,
363
                    const Value *src_value,
364
                    const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
365
                    unsigned int new_format, Flags flag) const
366
0
  {
367
0
    // Filter by new format.
368
0
    if (!(new_format & flag)) return true;
369
0
370
0
    Value       *dst_value = c->copy (*src_value);
371
0
372
0
    if (!dst_value) return false;
373
0
    if (*dst_value == 0) return true;
374
0
375
0
    *dst_value = 0;
376
0
    c->push ();
377
0
    if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
378
0
    {
379
0
      c->add_link (*dst_value, c->pop_pack ());
380
0
      return true;
381
0
    }
382
0
    else
383
0
    {
384
0
      c->pop_discard ();
385
0
      return false;
386
0
    }
387
0
  }
388
389
  static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
390
0
  {
391
0
    if (worked) *worked |= bool (*value);
392
0
    return *reinterpret_cast<const HBINT16 *> (value);
393
0
  }
394
395
  public:
396
397
  bool has_device () const
398
0
  {
399
0
    unsigned int format = *this;
400
0
    return (format & devices) != 0;
401
0
  }
402
403
  bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
404
0
  {
405
0
    TRACE_SANITIZE (this);
406
407
0
    if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
408
409
0
    if (c->lazy_some_gpos)
410
0
      return_trace (true);
411
412
0
    return_trace (!has_device () || sanitize_value_devices (c, base, values));
413
0
  }
414
415
  bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
416
0
  {
417
0
    TRACE_SANITIZE (this);
418
0
    unsigned size = get_size ();
419
420
0
    if (!c->check_range (values, count, size)) return_trace (false);
421
422
0
    if (c->lazy_some_gpos)
423
0
      return_trace (true);
424
425
0
    hb_barrier ();
426
0
    return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
427
0
  }
428
429
  /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
430
  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
431
0
  {
432
0
    TRACE_SANITIZE (this);
433
434
0
    if (!has_device ()) return_trace (true);
435
436
0
    for (unsigned int i = 0; i < count; i++) {
437
0
      if (!sanitize_value_devices (c, base, values))
438
0
        return_trace (false);
439
0
      values = &StructAtOffset<const Value> (values, stride);
440
0
    }
441
442
0
    return_trace (true);
443
0
  }
444
445
 private:
446
447
  void should_drop (Value value, Flags flag, unsigned int* format) const
448
0
  {
449
0
    if (value) return;
450
0
    *format = *format & ~flag;
451
0
  }
452
453
  void update_var_flag (const Value* value, Flags flag,
454
                        unsigned int* format, const ValueBase *base,
455
                        const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
456
0
  {
457
0
    if (*value)
458
0
    {
459
0
      unsigned varidx = (base + get_device (value)).get_variation_index ();
460
0
      hb_pair_t<unsigned, int> *varidx_delta;
461
0
      if (varidx_delta_map->has (varidx, &varidx_delta))
462
0
      {
463
0
        /* A device-only value needs a base field for its instanced delta. */
464
0
        if (varidx_delta->second)
465
0
          *format |= flag >> 4;
466
0
        if (varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
467
0
          return;
468
0
      }
469
0
    }
470
0
    *format = *format & ~flag;
471
0
  }
472
};
473
474
}
475
}
476
}
477
478
#endif  // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH