Coverage Report

Created: 2025-11-16 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pango/subprojects/harfbuzz/src/hb-aat-layout-kerx-table.hh
Line
Count
Source
1
/*
2
 * Copyright © 2018  Ebrahim Byagowi
3
 * Copyright © 2018  Google, Inc.
4
 *
5
 *  This is part of HarfBuzz, a text shaping library.
6
 *
7
 * Permission is hereby granted, without written agreement and without
8
 * license or royalty fees, to use, copy, modify, and distribute this
9
 * software and its documentation for any purpose, provided that the
10
 * above copyright notice and the following two paragraphs appear in
11
 * all copies of this software.
12
 *
13
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17
 * DAMAGE.
18
 *
19
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24
 *
25
 * Google Author(s): Behdad Esfahbod
26
 */
27
28
#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
29
#define HB_AAT_LAYOUT_KERX_TABLE_HH
30
31
#include "hb-kern.hh"
32
#include "hb-aat-layout-ankr-table.hh"
33
34
/*
35
 * kerx -- Extended Kerning
36
 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
37
 */
38
#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x')
39
40
41
namespace AAT {
42
43
using namespace OT;
44
45
46
static inline int
47
kerxTupleKern (int value,
48
         unsigned int tupleCount,
49
         const void *base,
50
         hb_aat_apply_context_t *c)
51
0
{
52
0
  if (likely (!tupleCount || !c)) return value;
53
54
0
  unsigned int offset = value;
55
0
  const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
56
0
  if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
57
0
  hb_barrier ();
58
0
  return *pv;
59
0
}
Unexecuted instantiation: hb-ot-face.cc:AAT::kerxTupleKern(int, unsigned int, void const*, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: hb-ot-layout.cc:AAT::kerxTupleKern(int, unsigned int, void const*, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: hb-aat-layout.cc:AAT::kerxTupleKern(int, unsigned int, void const*, AAT::hb_aat_apply_context_t*)
60
61
62
struct hb_glyph_pair_t
63
{
64
  hb_codepoint_t left;
65
  hb_codepoint_t right;
66
};
67
68
struct KernPair
69
{
70
0
  int get_kerning () const { return value; }
71
72
  int cmp (const hb_glyph_pair_t &o) const
73
0
  {
74
0
    int ret = left.cmp (o.left);
75
0
    if (ret) return ret;
76
0
    return right.cmp (o.right);
77
0
  }
78
79
  bool sanitize (hb_sanitize_context_t *c) const
80
0
  {
81
0
    TRACE_SANITIZE (this);
82
0
    return_trace (c->check_struct (this));
83
0
  }
84
85
  protected:
86
  HBGlyphID16 left;
87
  HBGlyphID16 right;
88
  FWORD   value;
89
  public:
90
  DEFINE_SIZE_STATIC (6);
91
};
92
93
template <typename KernSubTableHeader>
94
struct KerxSubTableFormat0
95
{
96
  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
97
       hb_aat_apply_context_t *c = nullptr) const
98
0
  {
99
0
    hb_glyph_pair_t pair = {left, right};
100
0
    int v = pairs.bsearch (pair).get_kerning ();
101
0
    return kerxTupleKern (v, header.tuple_count (), this, c);
102
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
103
104
  bool apply (hb_aat_apply_context_t *c) const
105
0
  {
106
0
    TRACE_APPLY (this);
107
108
0
    if (!c->plan->requested_kerning)
109
0
      return false;
110
111
0
    if (header.coverage & header.Backwards)
112
0
      return false;
113
114
0
    accelerator_t accel (*this, c);
115
0
    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
116
0
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
117
118
0
    return_trace (true);
119
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
120
121
  struct accelerator_t
122
  {
123
    const KerxSubTableFormat0 &table;
124
    hb_aat_apply_context_t *c;
125
126
    accelerator_t (const KerxSubTableFormat0 &table_,
127
       hb_aat_apply_context_t *c_) :
128
0
         table (table_), c (c_) {}
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
129
130
    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
131
0
    { return table.get_kerning (left, right, c); }
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
132
  };
133
134
135
  bool sanitize (hb_sanitize_context_t *c) const
136
0
  {
137
0
    TRACE_SANITIZE (this);
138
0
    return_trace (likely (pairs.sanitize (c)));
139
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat0<AAT::KerxSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernOTSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat0<OT::KernAATSubTableHeader>::sanitize(hb_sanitize_context_t*) const
140
141
  protected:
142
  KernSubTableHeader  header;
143
  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
144
      pairs;  /* Sorted kern records. */
145
  public:
146
  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
147
};
148
149
150
template <bool extended>
151
struct Format1Entry;
152
153
template <>
154
struct Format1Entry<true>
155
{
156
  enum Flags
157
  {
158
    Push    = 0x8000, /* If set, push this glyph on the kerning stack. */
159
    DontAdvance   = 0x4000, /* If set, don't advance to the next glyph
160
           * before going to the new state. */
161
    Reset   = 0x2000, /* If set, reset the kerning data (clear the stack) */
162
    Reserved    = 0x1FFF, /* Not used; set to 0. */
163
  };
164
165
  struct EntryData
166
  {
167
    HBUINT16  kernActionIndex;/* Index into the kerning value array. If
168
         * this index is 0xFFFF, then no kerning
169
         * is to be performed. */
170
    public:
171
    DEFINE_SIZE_STATIC (2);
172
  };
173
174
  static bool performAction (const Entry<EntryData> &entry)
175
0
  { return entry.data.kernActionIndex != 0xFFFF; }
176
177
  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
178
0
  { return entry.data.kernActionIndex; }
179
};
180
template <>
181
struct Format1Entry<false>
182
{
183
  enum Flags
184
  {
185
    Push    = 0x8000, /* If set, push this glyph on the kerning stack. */
186
    DontAdvance   = 0x4000, /* If set, don't advance to the next glyph
187
           * before going to the new state. */
188
    Offset    = 0x3FFF, /* Byte offset from beginning of subtable to the
189
           * value table for the glyphs on the kerning stack. */
190
191
    Reset   = 0x0000, /* Not supported? */
192
  };
193
194
  typedef void EntryData;
195
196
  static bool performAction (const Entry<EntryData> &entry)
197
0
  { return entry.flags & Offset; }
198
199
  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
200
0
  { return entry.flags & Offset; }
201
};
202
203
template <typename KernSubTableHeader>
204
struct KerxSubTableFormat1
205
{
206
  typedef typename KernSubTableHeader::Types Types;
207
  typedef typename Types::HBUINT HBUINT;
208
209
  typedef Format1Entry<Types::extended> Format1EntryT;
210
  typedef typename Format1EntryT::EntryData EntryData;
211
212
  struct driver_context_t
213
  {
214
    static constexpr bool in_place = true;
215
    enum
216
    {
217
      DontAdvance = Format1EntryT::DontAdvance,
218
    };
219
220
    driver_context_t (const KerxSubTableFormat1 *table_,
221
          hb_aat_apply_context_t *c_) :
222
0
  c (c_),
223
0
  table (table_),
224
  /* Apparently the offset kernAction is from the beginning of the state-machine,
225
   * similar to offsets in morx table, NOT from beginning of this table, like
226
   * other subtables in kerx.  Discovered via testing. */
227
0
  kernAction (&table->machine + table->kernAction),
228
0
  depth (0),
229
0
  crossStream (table->header.coverage & table->header.CrossStream) {}
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader>::driver_context_t::driver_context_t(AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader> const*, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader>::driver_context_t::driver_context_t(AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader> const*, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader>::driver_context_t::driver_context_t(AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader> const*, AAT::hb_aat_apply_context_t*)
230
231
    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
232
      const Entry<EntryData> &entry)
233
0
    { return Format1EntryT::performAction (entry); }
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader>::driver_context_t::is_actionable(AAT::StateTableDriver<AAT::ObsoleteTypes, void>*, AAT::Entry<void> const&)
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader>::driver_context_t::is_actionable(AAT::StateTableDriver<AAT::ObsoleteTypes, void>*, AAT::Entry<void> const&)
Unexecuted instantiation: AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader>::driver_context_t::is_actionable(AAT::StateTableDriver<AAT::ExtendedTypes, AAT::Format1Entry<true>::EntryData>*, AAT::Entry<AAT::Format1Entry<true>::EntryData> const&)
234
    void transition (StateTableDriver<Types, EntryData> *driver,
235
         const Entry<EntryData> &entry)
236
0
    {
237
0
      hb_buffer_t *buffer = driver->buffer;
238
0
      unsigned int flags = entry.flags;
239
240
0
      if (flags & Format1EntryT::Reset)
241
0
  depth = 0;
242
243
0
      if (flags & Format1EntryT::Push)
244
0
      {
245
0
  if (likely (depth < ARRAY_LENGTH (stack)))
246
0
    stack[depth++] = buffer->idx;
247
0
  else
248
0
    depth = 0; /* Probably not what CoreText does, but better? */
249
0
      }
250
251
0
      if (Format1EntryT::performAction (entry) && depth)
252
0
      {
253
0
  unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
254
255
0
  unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
256
0
  kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
257
0
  const FWORD *actions = &kernAction[kern_idx];
258
0
  if (!c->sanitizer.check_array (actions, depth, tuple_count))
259
0
  {
260
0
    depth = 0;
261
0
    return;
262
0
  }
263
0
  hb_barrier ();
264
265
0
  hb_mask_t kern_mask = c->plan->kern_mask;
266
267
  /* From Apple 'kern' spec:
268
   * "Each pops one glyph from the kerning stack and applies the kerning value to it.
269
   * The end of the list is marked by an odd value... */
270
0
  bool last = false;
271
0
  while (!last && depth)
272
0
  {
273
0
    unsigned int idx = stack[--depth];
274
0
    int v = *actions;
275
0
    actions += tuple_count;
276
0
    if (idx >= buffer->len) continue;
277
278
    /* "The end of the list is marked by an odd value..." */
279
0
    last = v & 1;
280
0
    v &= ~1;
281
282
0
    hb_glyph_position_t &o = buffer->pos[idx];
283
284
0
    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
285
0
    {
286
0
      if (crossStream)
287
0
      {
288
        /* The following flag is undocumented in the spec, but described
289
         * in the 'kern' table example. */
290
0
        if (v == -0x8000)
291
0
        {
292
0
    o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
293
0
    o.attach_chain() = 0;
294
0
    o.y_offset = 0;
295
0
        }
296
0
        else if (o.attach_type())
297
0
        {
298
0
    o.y_offset += c->font->em_scale_y (v);
299
0
    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
300
0
        }
301
0
      }
302
0
      else if (buffer->info[idx].mask & kern_mask)
303
0
      {
304
0
        o.x_advance += c->font->em_scale_x (v);
305
0
        o.x_offset += c->font->em_scale_x (v);
306
0
      }
307
0
    }
308
0
    else
309
0
    {
310
0
      if (crossStream)
311
0
      {
312
        /* CoreText doesn't do crossStream kerning in vertical.  We do. */
313
0
        if (v == -0x8000)
314
0
        {
315
0
    o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
316
0
    o.attach_chain() = 0;
317
0
    o.x_offset = 0;
318
0
        }
319
0
        else if (o.attach_type())
320
0
        {
321
0
    o.x_offset += c->font->em_scale_x (v);
322
0
    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
323
0
        }
324
0
      }
325
0
      else if (buffer->info[idx].mask & kern_mask)
326
0
      {
327
0
        o.y_advance += c->font->em_scale_y (v);
328
0
        o.y_offset += c->font->em_scale_y (v);
329
0
      }
330
0
    }
331
0
  }
332
0
      }
333
0
    }
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader>::driver_context_t::transition(AAT::StateTableDriver<AAT::ObsoleteTypes, void>*, AAT::Entry<void> const&)
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader>::driver_context_t::transition(AAT::StateTableDriver<AAT::ObsoleteTypes, void>*, AAT::Entry<void> const&)
Unexecuted instantiation: AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader>::driver_context_t::transition(AAT::StateTableDriver<AAT::ExtendedTypes, AAT::Format1Entry<true>::EntryData>*, AAT::Entry<AAT::Format1Entry<true>::EntryData> const&)
334
335
    private:
336
    hb_aat_apply_context_t *c;
337
    const KerxSubTableFormat1 *table;
338
    const UnsizedArrayOf<FWORD> &kernAction;
339
    unsigned int stack[8];
340
    unsigned int depth;
341
    bool crossStream;
342
  };
343
344
  bool apply (hb_aat_apply_context_t *c) const
345
0
  {
346
0
    TRACE_APPLY (this);
347
348
0
    if (!c->plan->requested_kerning &&
349
0
  !(header.coverage & header.CrossStream))
350
0
      return false;
351
352
0
    driver_context_t dc (this, c);
353
354
0
    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
355
0
    driver.drive (&dc, c);
356
357
0
    return_trace (true);
358
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
359
360
  bool sanitize (hb_sanitize_context_t *c) const
361
0
  {
362
0
    TRACE_SANITIZE (this);
363
    /* The rest of array sanitizations are done at run-time. */
364
0
    return_trace (likely (c->check_struct (this) &&
365
0
        machine.sanitize (c)));
366
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat1<AAT::KerxSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernOTSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat1<OT::KernAATSubTableHeader>::sanitize(hb_sanitize_context_t*) const
367
368
  protected:
369
  KernSubTableHeader        header;
370
  StateTable<Types, EntryData>      machine;
371
  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT> kernAction;
372
  public:
373
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
374
};
375
376
template <typename KernSubTableHeader>
377
struct KerxSubTableFormat2
378
{
379
  typedef typename KernSubTableHeader::Types Types;
380
  typedef typename Types::HBUINT HBUINT;
381
382
  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
383
       hb_aat_apply_context_t *c) const
384
0
  {
385
0
    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
386
0
    unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
387
0
    unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
388
389
0
    const UnsizedArrayOf<FWORD> &arrayZ = this+array;
390
0
    unsigned int kern_idx = l + r;
391
0
    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
392
0
    const FWORD *v = &arrayZ[kern_idx];
393
0
    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
394
0
    hb_barrier ();
395
396
0
    return kerxTupleKern (*v, header.tuple_count (), this, c);
397
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader>::get_kerning(unsigned int, unsigned int, AAT::hb_aat_apply_context_t*) const
398
399
  bool apply (hb_aat_apply_context_t *c) const
400
0
  {
401
0
    TRACE_APPLY (this);
402
403
0
    if (!c->plan->requested_kerning)
404
0
      return false;
405
406
0
    if (header.coverage & header.Backwards)
407
0
      return false;
408
409
0
    accelerator_t accel (*this, c);
410
0
    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
411
0
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
412
413
0
    return_trace (true);
414
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader>::apply(AAT::hb_aat_apply_context_t*) const
415
416
  struct accelerator_t
417
  {
418
    const KerxSubTableFormat2 &table;
419
    hb_aat_apply_context_t *c;
420
421
    accelerator_t (const KerxSubTableFormat2 &table_,
422
       hb_aat_apply_context_t *c_) :
423
0
         table (table_), c (c_) {}
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
Unexecuted instantiation: AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader>::accelerator_t::accelerator_t(AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader> const&, AAT::hb_aat_apply_context_t*)
424
425
    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
426
0
    { return table.get_kerning (left, right, c); }
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader>::accelerator_t::get_kerning(unsigned int, unsigned int) const
427
  };
428
429
  bool sanitize (hb_sanitize_context_t *c) const
430
0
  {
431
0
    TRACE_SANITIZE (this);
432
0
    return_trace (likely (c->check_struct (this) &&
433
0
        leftClassTable.sanitize (c, this) &&
434
0
        rightClassTable.sanitize (c, this) &&
435
0
        hb_barrier () &&
436
0
        c->check_range (this, array)));
437
0
  }
Unexecuted instantiation: AAT::KerxSubTableFormat2<AAT::KerxSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernOTSubTableHeader>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxSubTableFormat2<OT::KernAATSubTableHeader>::sanitize(hb_sanitize_context_t*) const
438
439
  protected:
440
  KernSubTableHeader  header;
441
  HBUINT    rowWidth; /* The width, in bytes, of a row in the table. */
442
  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
443
      leftClassTable; /* Offset from beginning of this subtable to
444
           * left-hand class table. */
445
  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
446
      rightClassTable;/* Offset from beginning of this subtable to
447
           * right-hand class table. */
448
  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT>
449
       array;   /* Offset from beginning of this subtable to
450
           * the start of the kerning array. */
451
  public:
452
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT));
453
};
454
455
template <typename KernSubTableHeader>
456
struct KerxSubTableFormat4
457
{
458
  typedef ExtendedTypes Types;
459
460
  struct EntryData
461
  {
462
    HBUINT16  ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
463
         * the action to perform. */
464
    public:
465
    DEFINE_SIZE_STATIC (2);
466
  };
467
468
  struct driver_context_t
469
  {
470
    static constexpr bool in_place = true;
471
    enum Flags
472
    {
473
      Mark    = 0x8000, /* If set, remember this glyph as the marked glyph. */
474
      DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
475
           * going to the new state. */
476
      Reserved    = 0x3FFF, /* Not used; set to 0. */
477
    };
478
479
    enum SubTableFlags
480
    {
481
      ActionType  = 0xC0000000, /* A two-bit field containing the action type. */
482
      Unused    = 0x3F000000, /* Unused - must be zero. */
483
      Offset    = 0x00FFFFFF, /* Masks the offset in bytes from the beginning
484
           * of the subtable to the beginning of the control
485
           * point table. */
486
    };
487
488
    driver_context_t (const KerxSubTableFormat4 *table,
489
          hb_aat_apply_context_t *c_) :
490
0
  c (c_),
491
0
  action_type ((table->flags & ActionType) >> 30),
492
0
  ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
493
0
  mark_set (false),
494
0
  mark (0) {}
495
496
    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
497
      const Entry<EntryData> &entry)
498
0
    { return entry.data.ankrActionIndex != 0xFFFF; }
499
    void transition (StateTableDriver<Types, EntryData> *driver,
500
         const Entry<EntryData> &entry)
501
0
    {
502
0
      hb_buffer_t *buffer = driver->buffer;
503
504
0
      if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
505
0
      {
506
0
  hb_glyph_position_t &o = buffer->cur_pos();
507
0
  switch (action_type)
508
0
  {
509
0
    case 0: /* Control Point Actions.*/
510
0
    {
511
      /* Indexed into glyph outline. */
512
      /* Each action (record in ankrData) contains two 16-bit fields, so we must
513
         double the ankrActionIndex to get the correct offset here. */
514
0
      const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
515
0
      if (!c->sanitizer.check_array (data, 2)) return;
516
0
      hb_barrier ();
517
0
      unsigned int markControlPoint = *data++;
518
0
      unsigned int currControlPoint = *data++;
519
0
      hb_position_t markX = 0;
520
0
      hb_position_t markY = 0;
521
0
      hb_position_t currX = 0;
522
0
      hb_position_t currY = 0;
523
0
      if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
524
0
                    markControlPoint,
525
0
                    HB_DIRECTION_LTR /*XXX*/,
526
0
                    &markX, &markY) ||
527
0
    !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
528
0
                    currControlPoint,
529
0
                    HB_DIRECTION_LTR /*XXX*/,
530
0
                    &currX, &currY))
531
0
        return;
532
533
0
      o.x_offset = markX - currX;
534
0
      o.y_offset = markY - currY;
535
0
    }
536
0
    break;
537
538
0
    case 1: /* Anchor Point Actions. */
539
0
    {
540
      /* Indexed into 'ankr' table. */
541
      /* Each action (record in ankrData) contains two 16-bit fields, so we must
542
         double the ankrActionIndex to get the correct offset here. */
543
0
      const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
544
0
      if (!c->sanitizer.check_array (data, 2)) return;
545
0
      hb_barrier ();
546
0
      unsigned int markAnchorPoint = *data++;
547
0
      unsigned int currAnchorPoint = *data++;
548
0
      const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
549
0
                  markAnchorPoint,
550
0
                  c->sanitizer.get_num_glyphs ());
551
0
      const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
552
0
                  currAnchorPoint,
553
0
                  c->sanitizer.get_num_glyphs ());
554
555
0
      o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
556
0
      o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
557
0
    }
558
0
    break;
559
560
0
    case 2: /* Control Point Coordinate Actions. */
561
0
    {
562
      /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex
563
         by 4 to get the correct offset for the given action. */
564
0
      const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
565
0
      if (!c->sanitizer.check_array (data, 4)) return;
566
0
      hb_barrier ();
567
0
      int markX = *data++;
568
0
      int markY = *data++;
569
0
      int currX = *data++;
570
0
      int currY = *data++;
571
572
0
      o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
573
0
      o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
574
0
    }
575
0
    break;
576
0
  }
577
0
  o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK;
578
0
  o.attach_chain() = (int) mark - (int) buffer->idx;
579
0
  buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
580
0
      }
581
582
0
      if (entry.flags & Mark)
583
0
      {
584
0
  mark_set = true;
585
0
  mark = buffer->idx;
586
0
      }
587
0
    }
588
589
    private:
590
    hb_aat_apply_context_t *c;
591
    unsigned int action_type;
592
    const HBUINT16 *ankrData;
593
    bool mark_set;
594
    unsigned int mark;
595
  };
596
597
  bool apply (hb_aat_apply_context_t *c) const
598
0
  {
599
0
    TRACE_APPLY (this);
600
601
0
    driver_context_t dc (this, c);
602
603
0
    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
604
0
    driver.drive (&dc, c);
605
606
0
    return_trace (true);
607
0
  }
608
609
  bool sanitize (hb_sanitize_context_t *c) const
610
0
  {
611
0
    TRACE_SANITIZE (this);
612
    /* The rest of array sanitizations are done at run-time. */
613
0
    return_trace (likely (c->check_struct (this) &&
614
0
        machine.sanitize (c)));
615
0
  }
616
617
  protected:
618
  KernSubTableHeader    header;
619
  StateTable<Types, EntryData>  machine;
620
  HBUINT32      flags;
621
  public:
622
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
623
};
624
625
template <typename KernSubTableHeader>
626
struct KerxSubTableFormat6
627
{
628
  enum Flags
629
  {
630
    ValuesAreLong = 0x00000001,
631
  };
632
633
0
  bool is_long () const { return flags & ValuesAreLong; }
634
635
  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
636
       hb_aat_apply_context_t *c) const
637
0
  {
638
0
    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
639
0
    if (is_long ())
640
0
    {
641
0
      const typename U::Long &t = u.l;
642
0
      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
643
0
      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
644
0
      unsigned int offset = l + r;
645
0
      if (unlikely (offset < l)) return 0; /* Addition overflow. */
646
0
      if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
647
0
      const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
648
0
      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
649
0
      hb_barrier ();
650
0
      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
651
0
    }
652
0
    else
653
0
    {
654
0
      const typename U::Short &t = u.s;
655
0
      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
656
0
      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
657
0
      unsigned int offset = l + r;
658
0
      const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
659
0
      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
660
0
      hb_barrier ();
661
0
      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
662
0
    }
663
0
  }
664
665
  bool apply (hb_aat_apply_context_t *c) const
666
0
  {
667
0
    TRACE_APPLY (this);
668
669
0
    if (!c->plan->requested_kerning)
670
0
      return false;
671
672
0
    if (header.coverage & header.Backwards)
673
0
      return false;
674
675
0
    accelerator_t accel (*this, c);
676
0
    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
677
0
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
678
679
0
    return_trace (true);
680
0
  }
681
682
  bool sanitize (hb_sanitize_context_t *c) const
683
0
  {
684
0
    TRACE_SANITIZE (this);
685
0
    return_trace (likely (c->check_struct (this) &&
686
0
        hb_barrier () &&
687
0
        (is_long () ?
688
0
         (
689
0
           u.l.rowIndexTable.sanitize (c, this) &&
690
0
           u.l.columnIndexTable.sanitize (c, this) &&
691
0
           c->check_range (this, u.l.array)
692
0
         ) : (
693
0
           u.s.rowIndexTable.sanitize (c, this) &&
694
0
           u.s.columnIndexTable.sanitize (c, this) &&
695
0
           c->check_range (this, u.s.array)
696
0
         )) &&
697
0
        (header.tuple_count () == 0 ||
698
0
         c->check_range (this, vector))));
699
0
  }
700
701
  struct accelerator_t
702
  {
703
    const KerxSubTableFormat6 &table;
704
    hb_aat_apply_context_t *c;
705
706
    accelerator_t (const KerxSubTableFormat6 &table_,
707
       hb_aat_apply_context_t *c_) :
708
0
         table (table_), c (c_) {}
709
710
    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
711
0
    { return table.get_kerning (left, right, c); }
712
  };
713
714
  protected:
715
  KernSubTableHeader    header;
716
  HBUINT32      flags;
717
  HBUINT16      rowCount;
718
  HBUINT16      columnCount;
719
  union U
720
  {
721
    struct Long
722
    {
723
      NNOffset32To<Lookup<HBUINT32>>    rowIndexTable;
724
      NNOffset32To<Lookup<HBUINT32>>    columnIndexTable;
725
      NNOffset32To<UnsizedArrayOf<FWORD32>> array;
726
    } l;
727
    struct Short
728
    {
729
      NNOffset32To<Lookup<HBUINT16>>    rowIndexTable;
730
      NNOffset32To<Lookup<HBUINT16>>    columnIndexTable;
731
      NNOffset32To<UnsizedArrayOf<FWORD>> array;
732
    } s;
733
  } u;
734
  NNOffset32To<UnsizedArrayOf<FWORD>> vector;
735
  public:
736
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
737
};
738
739
740
struct KerxSubTableHeader
741
{
742
  typedef ExtendedTypes Types;
743
744
0
  unsigned   tuple_count () const { return tupleCount; }
745
0
  bool     is_horizontal () const { return !(coverage & Vertical); }
746
747
  enum Coverage
748
  {
749
    Vertical  = 0x80000000u,  /* Set if table has vertical kerning values. */
750
    CrossStream = 0x40000000u,  /* Set if table has cross-stream kerning values. */
751
    Variation = 0x20000000u,  /* Set if table has variation kerning values. */
752
    Backwards = 0x10000000u,  /* If clear, process the glyphs forwards, that
753
         * is, from first to last in the glyph stream.
754
         * If we, process them from last to first.
755
         * This flag only applies to state-table based
756
         * 'kerx' subtables (types 1 and 4). */
757
    Reserved  = 0x0FFFFF00u,  /* Reserved, set to zero. */
758
    SubtableType= 0x000000FFu,  /* Subtable type. */
759
  };
760
761
  bool sanitize (hb_sanitize_context_t *c) const
762
0
  {
763
0
    TRACE_SANITIZE (this);
764
0
    return_trace (c->check_struct (this));
765
0
  }
766
767
  public:
768
  HBUINT32  length;
769
  HBUINT32  coverage;
770
  HBUINT32  tupleCount;
771
  public:
772
  DEFINE_SIZE_STATIC (12);
773
};
774
775
struct KerxSubTable
776
{
777
  friend struct kerx;
778
779
0
  unsigned int get_size () const { return u.header.length; }
780
0
  unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
781
782
  template <typename context_t, typename ...Ts>
783
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
784
0
  {
785
0
    unsigned int subtable_type = get_type ();
786
0
    TRACE_DISPATCH (this, subtable_type);
787
0
    switch (subtable_type) {
788
0
    case 0: return_trace (c->dispatch (u.format0, std::forward<Ts> (ds)...));
789
0
    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
790
0
    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
791
0
    case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
792
0
    case 6: return_trace (c->dispatch (u.format6, std::forward<Ts> (ds)...));
793
0
    default:  return_trace (c->default_return_value ());
794
0
    }
795
0
  }
Unexecuted instantiation: hb_sanitize_context_t::return_t AAT::KerxSubTable::dispatch<hb_sanitize_context_t>(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::hb_aat_apply_context_t::return_t AAT::KerxSubTable::dispatch<AAT::hb_aat_apply_context_t>(AAT::hb_aat_apply_context_t*) const
796
797
  bool sanitize (hb_sanitize_context_t *c) const
798
0
  {
799
0
    TRACE_SANITIZE (this);
800
0
    if (!(u.header.sanitize (c) &&
801
0
    hb_barrier () &&
802
0
    u.header.length >= u.header.static_size &&
803
0
    c->check_range (this, u.header.length)))
804
0
      return_trace (false);
805
806
0
    return_trace (dispatch (c));
807
0
  }
808
809
  public:
810
  union {
811
  KerxSubTableHeader        header;
812
  KerxSubTableFormat0<KerxSubTableHeader> format0;
813
  KerxSubTableFormat1<KerxSubTableHeader> format1;
814
  KerxSubTableFormat2<KerxSubTableHeader> format2;
815
  KerxSubTableFormat4<KerxSubTableHeader> format4;
816
  KerxSubTableFormat6<KerxSubTableHeader> format6;
817
  } u;
818
  public:
819
  DEFINE_SIZE_MIN (12);
820
};
821
822
823
/*
824
 * The 'kerx' Table
825
 */
826
827
template <typename T>
828
struct KerxTable
829
{
830
  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
831
0
  const T* thiz () const { return static_cast<const T *> (this); }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::thiz() const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::thiz() const
Unexecuted instantiation: AAT::KerxTable<AAT::kerx>::thiz() const
832
833
  bool has_state_machine () const
834
0
  {
835
0
    typedef typename T::SubTable SubTable;
836
837
0
    const SubTable *st = &thiz()->firstSubTable;
838
0
    unsigned int count = thiz()->tableCount;
839
0
    for (unsigned int i = 0; i < count; i++)
840
0
    {
841
0
      if (st->get_type () == 1)
842
0
  return true;
843
0
      st = &StructAfter<SubTable> (*st);
844
0
    }
845
0
    return false;
846
0
  }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::has_state_machine() const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::has_state_machine() const
847
848
  bool has_cross_stream () const
849
0
  {
850
0
    typedef typename T::SubTable SubTable;
851
852
0
    const SubTable *st = &thiz()->firstSubTable;
853
0
    unsigned int count = thiz()->tableCount;
854
0
    for (unsigned int i = 0; i < count; i++)
855
0
    {
856
0
      if (st->u.header.coverage & st->u.header.CrossStream)
857
0
  return true;
858
0
      st = &StructAfter<SubTable> (*st);
859
0
    }
860
0
    return false;
861
0
  }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::has_cross_stream() const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::has_cross_stream() const
862
863
  int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
864
0
  {
865
0
    typedef typename T::SubTable SubTable;
866
0
867
0
    int v = 0;
868
0
    const SubTable *st = &thiz()->firstSubTable;
869
0
    unsigned int count = thiz()->tableCount;
870
0
    for (unsigned int i = 0; i < count; i++)
871
0
    {
872
0
      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
873
0
    !st->u.header.is_horizontal ())
874
0
  continue;
875
0
      v += st->get_kerning (left, right);
876
0
      st = &StructAfter<SubTable> (*st);
877
0
    }
878
0
    return v;
879
0
  }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::get_h_kerning(unsigned int, unsigned int) const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::get_h_kerning(unsigned int, unsigned int) const
880
881
  bool apply (AAT::hb_aat_apply_context_t *c) const
882
0
  {
883
0
    c->buffer->unsafe_to_concat ();
884
885
0
    typedef typename T::SubTable SubTable;
886
887
0
    bool ret = false;
888
0
    bool seenCrossStream = false;
889
0
    c->set_lookup_index (0);
890
0
    const SubTable *st = &thiz()->firstSubTable;
891
0
    unsigned int count = thiz()->tableCount;
892
0
    for (unsigned int i = 0; i < count; i++)
893
0
    {
894
0
      bool reverse;
895
896
0
      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
897
0
  goto skip;
898
899
0
      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
900
0
  goto skip;
901
902
0
      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
903
0
    HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
904
905
0
      if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index))
906
0
  goto skip;
907
908
0
      if (!seenCrossStream &&
909
0
    (st->u.header.coverage & st->u.header.CrossStream))
910
0
      {
911
  /* Attach all glyphs into a chain. */
912
0
  seenCrossStream = true;
913
0
  hb_glyph_position_t *pos = c->buffer->pos;
914
0
  unsigned int count = c->buffer->len;
915
0
  for (unsigned int i = 0; i < count; i++)
916
0
  {
917
0
    pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE;
918
0
    pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
919
    /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
920
     * since there needs to be a non-zero attachment for post-positioning to
921
     * be needed. */
922
0
  }
923
0
      }
924
925
0
      if (reverse)
926
0
  c->buffer->reverse ();
927
928
0
      {
929
  /* See comment in sanitize() for conditional here. */
930
0
  hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
931
0
  ret |= st->dispatch (c);
932
0
      }
933
934
0
      if (reverse)
935
0
  c->buffer->reverse ();
936
937
0
      (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index);
938
939
0
    skip:
940
0
      st = &StructAfter<SubTable> (*st);
941
0
      c->set_lookup_index (c->lookup_index + 1);
942
0
    }
943
944
0
    return ret;
945
0
  }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::apply(AAT::hb_aat_apply_context_t*) const
Unexecuted instantiation: AAT::KerxTable<AAT::kerx>::apply(AAT::hb_aat_apply_context_t*) const
946
947
  bool sanitize (hb_sanitize_context_t *c) const
948
0
  {
949
0
    TRACE_SANITIZE (this);
950
0
    if (unlikely (!(thiz()->version.sanitize (c) &&
951
0
        hb_barrier () &&
952
0
        (unsigned) thiz()->version >= (unsigned) T::minVersion &&
953
0
        thiz()->tableCount.sanitize (c))))
954
0
      return_trace (false);
955
956
0
    typedef typename T::SubTable SubTable;
957
958
0
    const SubTable *st = &thiz()->firstSubTable;
959
0
    unsigned int count = thiz()->tableCount;
960
0
    for (unsigned int i = 0; i < count; i++)
961
0
    {
962
0
      if (unlikely (!st->u.header.sanitize (c)))
963
0
  return_trace (false);
964
0
      hb_barrier ();
965
      /* OpenType kern table has 2-byte subtable lengths.  That's limiting.
966
       * MS implementation also only supports one subtable, of format 0,
967
       * anyway.  Certain versions of some fonts, like Calibry, contain
968
       * kern subtable that exceeds 64kb.  Looks like, the subtable length
969
       * is simply ignored.  Which makes sense.  It's only needed if you
970
       * have multiple subtables.  To handle such fonts, we just ignore
971
       * the length for the last subtable. */
972
0
      hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr);
973
974
0
      if (unlikely (!st->sanitize (c)))
975
0
  return_trace (false);
976
977
0
      st = &StructAfter<SubTable> (*st);
978
0
    }
979
980
0
    return_trace (true);
981
0
  }
Unexecuted instantiation: AAT::KerxTable<OT::KernOT>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxTable<OT::KernAAT>::sanitize(hb_sanitize_context_t*) const
Unexecuted instantiation: AAT::KerxTable<AAT::kerx>::sanitize(hb_sanitize_context_t*) const
982
};
983
984
struct kerx : KerxTable<kerx>
985
{
986
  friend struct KerxTable<kerx>;
987
988
  static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx;
989
  static constexpr unsigned minVersion = 2u;
990
991
  typedef KerxSubTableHeader SubTableHeader;
992
  typedef SubTableHeader::Types Types;
993
  typedef KerxSubTable SubTable;
994
995
0
  bool has_data () const { return version; }
996
997
  protected:
998
  HBUINT16  version;  /* The version number of the extended kerning table
999
         * (currently 2, 3, or 4). */
1000
  HBUINT16  unused;   /* Set to 0. */
1001
  HBUINT32  tableCount; /* The number of subtables included in the extended kerning
1002
         * table. */
1003
  SubTable  firstSubTable;  /* Subtables. */
1004
/*subtableGlyphCoverageArray*/  /* Only if version >= 3. We don't use. */
1005
1006
  public:
1007
  DEFINE_SIZE_MIN (8);
1008
};
1009
1010
1011
} /* namespace AAT */
1012
1013
1014
#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */