Coverage Report

Created: 2026-08-31 07:00

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