hb-aat-layout-kerx-table.hh 28.3 KB
Newer Older
1 2
/*
 * Copyright © 2018  Ebrahim Byagowi
3
 * Copyright © 2018  Google, Inc.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
#define HB_AAT_LAYOUT_KERX_TABLE_HH

31
#include "hb-kern.hh"
B
Behdad Esfahbod 已提交
32
#include "hb-aat-layout-ankr-table.hh"
33

34 35 36 37
/*
 * kerx -- Extended Kerning
 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
 */
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 47 48 49 50 51 52 53 54 55
static inline int
kerxTupleKern (int value,
	       unsigned int tupleCount,
	       const void *base,
	       hb_aat_apply_context_t *c)
{
  if (likely (!tupleCount)) return value;

  unsigned int offset = value;
  const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
56
  if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
57 58 59 60
  return *pv;
}


B
Behdad Esfahbod 已提交
61
struct hb_glyph_pair_t
B
Behdad Esfahbod 已提交
62
{
B
Behdad Esfahbod 已提交
63 64 65
  hb_codepoint_t left;
  hb_codepoint_t right;
};
B
Behdad Esfahbod 已提交
66

B
Behdad Esfahbod 已提交
67 68 69 70 71 72
struct KernPair
{
  inline int get_kerning (void) const
  { return value; }

  inline int cmp (const hb_glyph_pair_t &o) const
73
  {
B
Behdad Esfahbod 已提交
74 75 76 77
    int ret = left.cmp (o.left);
    if (ret) return ret;
    return right.cmp (o.right);
  }
78

B
Behdad Esfahbod 已提交
79 80 81
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
82
    return_trace (c->check_struct (this));
B
Behdad Esfahbod 已提交
83 84
  }

B
Behdad Esfahbod 已提交
85 86 87 88
  protected:
  GlyphID	left;
  GlyphID	right;
  FWORD		value;
B
Behdad Esfahbod 已提交
89
  public:
B
Behdad Esfahbod 已提交
90
  DEFINE_SIZE_STATIC (6);
B
Behdad Esfahbod 已提交
91 92
};

93
template <typename KernSubTableHeader>
B
Behdad Esfahbod 已提交
94
struct KerxSubTableFormat0
95
{
B
Behdad Esfahbod 已提交
96 97 98 99 100 101 102 103
  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
  {
    hb_glyph_pair_t pair = {left, right};
    int i = pairs.bsearch (pair);
    if (i == -1) return 0;
    return pairs[i].get_kerning ();
  }

104 105
  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
			  hb_aat_apply_context_t *c) const
106
  {
B
Behdad Esfahbod 已提交
107 108
    hb_glyph_pair_t pair = {left, right};
    int i = pairs.bsearch (pair);
109 110
    if (i == -1) return 0;
    int v = pairs[i].get_kerning ();
B
Behdad Esfahbod 已提交
111
    return kerxTupleKern (v, header.tuple_count (), this, c);
112 113
  }

114 115 116 117
  inline bool apply (hb_aat_apply_context_t *c) const
  {
    TRACE_APPLY (this);

118 119 120
    if (!c->plan->requested_kerning)
      return false;

121
    if (header.coverage & (header.CrossStream | header.Backwards))
122 123
      return false;

124 125
    accelerator_t accel (*this, c);
    hb_kern_machine_t<accelerator_t> machine (accel);
B
Behdad Esfahbod 已提交
126
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
127 128 129

    return_trace (true);
  }
130

131 132 133 134 135 136 137 138 139 140 141 142 143 144
  struct accelerator_t
  {
    const KerxSubTableFormat0 &table;
    hb_aat_apply_context_t *c;

    inline accelerator_t (const KerxSubTableFormat0 &table_,
			  hb_aat_apply_context_t *c_) :
			    table (table_), c (c_) {}

    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
    { return table.get_kerning (left, right, c); }
  };


145 146 147
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
148
    return_trace (likely (pairs.sanitize (c)));
149 150 151
  }

  protected:
152
  KernSubTableHeader	header;
B
Behdad Esfahbod 已提交
153
  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
B
Behdad Esfahbod 已提交
154
			pairs;	/* Sorted kern records. */
155
  public:
156
  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
157 158
};

B
Behdad Esfahbod 已提交
159 160 161 162 163 164

template <bool extended>
struct Format1Entry;

template <>
struct Format1Entry<true>
165
{
B
Behdad Esfahbod 已提交
166 167 168 169 170 171 172 173 174
  enum Flags
  {
    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
					 * before going to the new state. */
    Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
    Reserved		= 0x1FFF,	/* Not used; set to 0. */
  };

175 176
  struct EntryData
  {
B
Behdad Esfahbod 已提交
177 178 179
    HBUINT16	kernActionIndex;/* Index into the kerning value array. If
				 * this index is 0xFFFF, then no kerning
				 * is to be performed. */
180 181 182
    public:
    DEFINE_SIZE_STATIC (2);
  };
183 184 185 186 187 188

  static inline bool performAction (const Entry<EntryData> *entry)
  { return entry->data.kernActionIndex != 0xFFFF; }

  static inline unsigned int kernActionIndex (const Entry<EntryData> *entry)
  { return entry->data.kernActionIndex; }
B
Behdad Esfahbod 已提交
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
};
template <>
struct Format1Entry<false>
{
  enum Flags
  {
    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
					 * before going to the new state. */
    Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
					 * value table for the glyphs on the kerning stack. */

    Reset		= 0x0000,	/* Not supported? */
  };

  typedef void EntryData;
205 206 207 208 209 210

  static inline bool performAction (const Entry<EntryData> *entry)
  { return entry->flags & Offset; }

  static inline unsigned int kernActionIndex (const Entry<EntryData> *entry)
  { return entry->flags & Offset; }
B
Behdad Esfahbod 已提交
211 212 213 214 215 216 217 218 219 220
};

template <typename KernSubTableHeader>
struct KerxSubTableFormat1
{
  typedef typename KernSubTableHeader::Types Types;
  typedef typename Types::HBUINT HBUINT;

  typedef Format1Entry<Types::extended> Format1EntryT;
  typedef typename Format1EntryT::EntryData EntryData;
221 222 223 224

  struct driver_context_t
  {
    static const bool in_place = true;
B
Behdad Esfahbod 已提交
225
    enum
226
    {
B
Behdad Esfahbod 已提交
227
      DontAdvance	= Format1EntryT::DontAdvance,
228 229
    };

230
    inline driver_context_t (const KerxSubTableFormat1 *table_,
B
Behdad Esfahbod 已提交
231 232
			     hb_aat_apply_context_t *c_) :
	c (c_),
233
	table (table_),
B
Behdad Esfahbod 已提交
234 235 236
	/* Apparently the offset kernAction is from the beginning of the state-machine,
	 * similar to offsets in morx table, NOT from beginning of this table, like
	 * other subtables in kerx.  Discovered via testing. */
B
Behdad Esfahbod 已提交
237
	kernAction (&table->machine + table->kernAction),
238
	depth (0),
239
	crossStream (table->header.coverage & table->header.CrossStream) {}
240 241 242 243 244 245 246 247 248 249 250

    /* TODO
     * 'kern' table has this pecularity, we don't currently implement.
     *
     * "Because the stateTableOffset in the state table header is (strictly
     * speaking) redundant, some 'kern' tables use it to record an initial
     * state where that should not be StartOfText. To determine if this is
     * done, calculate what the stateTableOffset should be. If it's different
     * from the actual stateTableOffset, use it as the initial state."
     */

251
    inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
252 253
			       const Entry<EntryData> *entry)
    {
254
      return Format1EntryT::performAction (entry);
255
    }
256
    inline bool transition (StateTableDriver<Types, EntryData> *driver,
257 258
			    const Entry<EntryData> *entry)
    {
B
Behdad Esfahbod 已提交
259 260 261
      hb_buffer_t *buffer = driver->buffer;
      unsigned int flags = entry->flags;

B
Behdad Esfahbod 已提交
262
      if (flags & Format1EntryT::Reset)
B
Behdad Esfahbod 已提交
263
	depth = 0;
B
Behdad Esfahbod 已提交
264

B
Behdad Esfahbod 已提交
265
      if (flags & Format1EntryT::Push)
B
Behdad Esfahbod 已提交
266
      {
B
Behdad Esfahbod 已提交
267
	if (likely (depth < ARRAY_LENGTH (stack)))
B
Behdad Esfahbod 已提交
268 269 270 271 272
	  stack[depth++] = buffer->idx;
	else
	  depth = 0; /* Probably not what CoreText does, but better? */
      }

273
      if (Format1EntryT::performAction (entry))
B
Behdad Esfahbod 已提交
274
      {
275 276 277
	unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
	kern_idx = Types::offsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
	const FWORD *actions = &kernAction[kern_idx];
B
Behdad Esfahbod 已提交
278
	if (!c->sanitizer.check_array (actions, depth))
B
Behdad Esfahbod 已提交
279 280 281 282 283
	{
	  depth = 0;
	  return false;
	}

284
	hb_mask_t kern_mask = c->plan->kern_mask;
285 286 287 288 289 290 291 292 293 294 295

	/* From Apple 'kern' spec:
	 * "Each pops one glyph from the kerning stack and applies the kerning value to it.
	 * The end of the list is marked by an odd value... */
	unsigned int i;
	for (i = 0; i < depth; i++)
	  if (actions[i] & 1)
	  {
	    i++;
	    break;
	  }
296 297
	unsigned int tuple_count = table->header.tuple_count ();
	tuple_count = tuple_count ? tuple_count : 1;
298
	for (; i; i--)
B
Behdad Esfahbod 已提交
299
	{
300
	  unsigned int idx = stack[depth - i];
B
Behdad Esfahbod 已提交
301 302
	  if (idx >= buffer->len) continue;

303
	  int v = actions[(i - 1) * tuple_count];
304

B
Behdad Esfahbod 已提交
305
	  /* "The end of the list is marked by an odd value..."  Ignore it. */
306 307
	  v &= ~1;

308 309
	  hb_glyph_position_t &o = buffer->pos[idx];

310 311
	  /* The following flag is undocumented in the spec, but described
	   * in the 'kern' table example. */
312
	  if (v == -0x8000)
313
	  {
314 315 316
	    o.attach_type() = ATTACH_TYPE_NONE;
	    o.attach_chain() = 0;
	    o.x_offset = o.y_offset = 0;
317
	  }
318
	  else if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
319
	  {
B
Behdad Esfahbod 已提交
320
	    if (crossStream)
B
Behdad Esfahbod 已提交
321
	    {
322 323 324 325 326
	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].y_offset)
	      {
		o.y_offset = c->font->em_scale_y (v);
		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
	      }
B
Behdad Esfahbod 已提交
327
	    }
B
Behdad Esfahbod 已提交
328
	    else if (buffer->info[idx].mask & kern_mask)
B
Behdad Esfahbod 已提交
329
	    {
B
Behdad Esfahbod 已提交
330
	      if (!buffer->pos[idx].x_offset)
331
	      {
B
Behdad Esfahbod 已提交
332 333
		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
334
	      }
B
Behdad Esfahbod 已提交
335 336 337 338 339 340 341
	    }
	  }
	  else
	  {
	    if (crossStream)
	    {
	      /* CoreText doesn't do crossStream kerning in vertical.  We do. */
342 343 344 345 346
	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].x_offset)
	      {
		o.x_offset = c->font->em_scale_x (v);
		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
	      }
B
Behdad Esfahbod 已提交
347 348 349 350
	    }
	    else if (buffer->info[idx].mask & kern_mask)
	    {
	      if (!buffer->pos[idx].y_offset)
351
	      {
B
Behdad Esfahbod 已提交
352 353
		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
354
	      }
B
Behdad Esfahbod 已提交
355
	    }
356
	  }
B
Behdad Esfahbod 已提交
357
	}
B
Behdad Esfahbod 已提交
358
	depth = 0;
B
Behdad Esfahbod 已提交
359
      }
360 361 362 363 364

      return true;
    }

    private:
B
Behdad Esfahbod 已提交
365
    hb_aat_apply_context_t *c;
366
    const KerxSubTableFormat1 *table;
B
Behdad Esfahbod 已提交
367 368 369
    const UnsizedArrayOf<FWORD> &kernAction;
    unsigned int stack[8];
    unsigned int depth;
370
    bool crossStream;
371 372
  };

373 374 375 376
  inline bool apply (hb_aat_apply_context_t *c) const
  {
    TRACE_APPLY (this);

377 378
    if (!c->plan->requested_kerning &&
	!(header.coverage & header.CrossStream))
379 380
      return false;

B
Behdad Esfahbod 已提交
381
    driver_context_t dc (this, c);
382

383
    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
384
    driver.drive (&dc);
385 386 387 388

    return_trace (true);
  }

389 390
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
391
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
392 393 394
    /* The rest of array sanitizations are done at run-time. */
    return_trace (likely (c->check_struct (this) &&
			  machine.sanitize (c)));
395 396 397
  }

  protected:
398
  KernSubTableHeader				header;
B
Behdad Esfahbod 已提交
399 400
  StateTable<Types, EntryData>			machine;
  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>kernAction;
401
  public:
B
Behdad Esfahbod 已提交
402
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
403 404
};

405
template <typename KernSubTableHeader>
406 407
struct KerxSubTableFormat2
{
B
Behdad Esfahbod 已提交
408 409 410
  typedef typename KernSubTableHeader::Types Types;
  typedef typename Types::HBUINT HBUINT;

B
Behdad Esfahbod 已提交
411
  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
412
			  hb_aat_apply_context_t *c) const
413
  {
414
    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
B
Behdad Esfahbod 已提交
415 416
    unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
    unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
417
    unsigned int offset = l + r;
B
Behdad Esfahbod 已提交
418 419
    const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
B
Behdad Esfahbod 已提交
420
    return kerxTupleKern (*v, header.tuple_count (), this, c);
421 422
  }

423 424 425 426
  inline bool apply (hb_aat_apply_context_t *c) const
  {
    TRACE_APPLY (this);

427 428 429
    if (!c->plan->requested_kerning)
      return false;

430
    if (header.coverage & (header.CrossStream | header.Backwards))
431 432
      return false;

433
    accelerator_t accel (*this, c);
B
Behdad Esfahbod 已提交
434 435
    hb_kern_machine_t<accelerator_t> machine (accel);
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
436 437 438 439

    return_trace (true);
  }

B
Behdad Esfahbod 已提交
440 441 442
  struct accelerator_t
  {
    const KerxSubTableFormat2 &table;
443
    hb_aat_apply_context_t *c;
B
Behdad Esfahbod 已提交
444 445

    inline accelerator_t (const KerxSubTableFormat2 &table_,
446 447
			  hb_aat_apply_context_t *c_) :
			    table (table_), c (c_) {}
B
Behdad Esfahbod 已提交
448 449

    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
450
    { return table.get_kerning (left, right, c); }
B
Behdad Esfahbod 已提交
451 452
  };

453 454 455 456 457 458 459 460 461
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (likely (c->check_struct (this) &&
			  leftClassTable.sanitize (c, this) &&
			  rightClassTable.sanitize (c, this) &&
			  c->check_range (this, array)));
  }

B
Behdad Esfahbod 已提交
462 463 464 465 466 467
  /* Note:
   * OT kern table specifies ClassTable as having 16-bit entries, whereas
   * AAT kern table specifies them as having 8bit entries.
   * I've not seen any fonts with this format in kern table.
   * We follow AAT. */

468
  protected:
469
  KernSubTableHeader	header;
B
Behdad Esfahbod 已提交
470 471
  HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
  OffsetTo<typename Types::ClassType, HBUINT, false>
B
Behdad Esfahbod 已提交
472 473
			leftClassTable;	/* Offset from beginning of this subtable to
					 * left-hand class table. */
B
Behdad Esfahbod 已提交
474
  OffsetTo<typename Types::ClassType, HBUINT, false>
B
Behdad Esfahbod 已提交
475 476
			rightClassTable;/* Offset from beginning of this subtable to
					 * right-hand class table. */
B
Behdad Esfahbod 已提交
477
  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>
B
Behdad Esfahbod 已提交
478
			 array;		/* Offset from beginning of this subtable to
B
Behdad Esfahbod 已提交
479
					 * the start of the kerning array. */
480
  public:
B
Behdad Esfahbod 已提交
481
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT));
482 483
};

484
template <typename KernSubTableHeader>
485 486
struct KerxSubTableFormat4
{
B
Behdad Esfahbod 已提交
487 488
  typedef ExtendedTypes Types;

B
Behdad Esfahbod 已提交
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
  struct EntryData
  {
    HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
				 * the action to perform. */
    public:
    DEFINE_SIZE_STATIC (2);
  };

  struct driver_context_t
  {
    static const bool in_place = true;
    enum Flags
    {
      Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
					 * going to the new state. */
      Reserved		= 0x3FFF,	/* Not used; set to 0. */
    };

    enum SubTableFlags
    {
      ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
      Unused		= 0x3F000000,	/* Unused - must be zero. */
      Offset		= 0x00FFFFFF,	/* Masks the offset in bytes from the beginning
					 * of the subtable to the beginning of the control
					 * point table. */
    };

    inline driver_context_t (const KerxSubTableFormat4 *table,
			     hb_aat_apply_context_t *c_) :
	c (c_),
	action_type ((table->flags & ActionType) >> 30),
	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
	mark_set (false),
	mark (0) {}

B
Behdad Esfahbod 已提交
525
    inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
B
Behdad Esfahbod 已提交
526 527 528 529
			       const Entry<EntryData> *entry)
    {
      return entry->data.ankrActionIndex != 0xFFFF;
    }
B
Behdad Esfahbod 已提交
530
    inline bool transition (StateTableDriver<Types, EntryData> *driver,
B
Behdad Esfahbod 已提交
531 532 533 534
			    const Entry<EntryData> *entry)
    {
      hb_buffer_t *buffer = driver->buffer;

535
      if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
B
Behdad Esfahbod 已提交
536
      {
537
	hb_glyph_position_t &o = buffer->cur_pos();
B
Behdad Esfahbod 已提交
538 539 540 541 542 543 544 545 546 547
	switch (action_type)
	{
	  case 0: /* Control Point Actions.*/
	  {
	    /* indexed into glyph outline. */
	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
	    if (!c->sanitizer.check_array (data, 2))
	      return false;
	    HB_UNUSED unsigned int markControlPoint = *data++;
	    HB_UNUSED unsigned int currControlPoint = *data++;
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
	    hb_position_t markX = 0;
	    hb_position_t markY = 0;
	    hb_position_t currX = 0;
	    hb_position_t currY = 0;
	    if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
							      markControlPoint,
							      HB_DIRECTION_LTR /*XXX*/,
							      &markX, &markY) ||
		!c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
							      currControlPoint,
							      HB_DIRECTION_LTR /*XXX*/,
							      &currX, &currY))
	      return true; /* True, such that the machine continues. */

	    o.x_offset = markX - currX;
	    o.y_offset = markY - currY;
B
Behdad Esfahbod 已提交
564 565 566 567 568 569 570 571 572
	  }
	  break;

	  case 1: /* Anchor Point Actions. */
	  {
	   /* Indexed into 'ankr' table. */
	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
	    if (!c->sanitizer.check_array (data, 2))
	      return false;
573 574
	    unsigned int markAnchorPoint = *data++;
	    unsigned int currAnchorPoint = *data++;
575 576 577 578 579 580 581 582
	    const Anchor markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
								 markAnchorPoint,
								 c->sanitizer.get_num_glyphs (),
								 c->ankr_end);
	    const Anchor currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
								 currAnchorPoint,
								 c->sanitizer.get_num_glyphs (),
								 c->ankr_end);
583

584 585
	    o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
	    o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
B
Behdad Esfahbod 已提交
586 587 588 589 590 591 592 593
	  }
	  break;

	  case 2: /* Control Point Coordinate Actions. */
	  {
	    const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
	    if (!c->sanitizer.check_array (data, 4))
	      return false;
594 595 596 597 598 599 600
	    int markX = *data++;
	    int markY = *data++;
	    int currX = *data++;
	    int currY = *data++;

	    o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
	    o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
B
Behdad Esfahbod 已提交
601 602 603
	  }
	  break;
	}
604 605 606
	o.attach_type() = ATTACH_TYPE_MARK;
	o.attach_chain() = (int) mark - (int) buffer->idx;
	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
B
Behdad Esfahbod 已提交
607 608
      }

B
Behdad Esfahbod 已提交
609
      if (entry->flags & Mark)
B
Behdad Esfahbod 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      {
	mark_set = true;
	mark = buffer->idx;
      }

      return true;
    }

    private:
    hb_aat_apply_context_t *c;
    unsigned int action_type;
    const HBUINT16 *ankrData;
    bool mark_set;
    unsigned int mark;
  };

626 627 628 629
  inline bool apply (hb_aat_apply_context_t *c) const
  {
    TRACE_APPLY (this);

B
Behdad Esfahbod 已提交
630 631
    driver_context_t dc (this, c);

B
Behdad Esfahbod 已提交
632
    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
B
Behdad Esfahbod 已提交
633
    driver.drive (&dc);
634 635 636 637

    return_trace (true);
  }

638 639 640
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
641
    /* The rest of array sanitizations are done at run-time. */
B
Behdad Esfahbod 已提交
642 643
    return_trace (likely (c->check_struct (this) &&
			  machine.sanitize (c)));
644 645 646
  }

  protected:
B
Behdad Esfahbod 已提交
647 648 649
  KernSubTableHeader		header;
  StateTable<Types, EntryData>	machine;
  HBUINT32			flags;
650
  public:
651
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
652 653
};

654
template <typename KernSubTableHeader>
655 656
struct KerxSubTableFormat6
{
657 658 659 660 661 662 663
  enum Flags
  {
    ValuesAreLong	= 0x00000001,
  };

  inline bool is_long (void) const { return flags & ValuesAreLong; }

B
Behdad Esfahbod 已提交
664
  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
665
			  hb_aat_apply_context_t *c) const
B
Behdad Esfahbod 已提交
666
  {
667
    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
B
Behdad Esfahbod 已提交
668 669
    if (is_long ())
    {
670
      const typename U::Long &t = u.l;
B
Behdad Esfahbod 已提交
671 672 673
      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
      unsigned int offset = l + r;
B
Behdad Esfahbod 已提交
674 675
      if (unlikely (offset < l)) return 0; /* Addition overflow. */
      if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
B
Behdad Esfahbod 已提交
676 677
      const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
B
Behdad Esfahbod 已提交
678
      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
B
Behdad Esfahbod 已提交
679 680 681
    }
    else
    {
682
      const typename U::Short &t = u.s;
B
Behdad Esfahbod 已提交
683 684 685
      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
      unsigned int offset = l + r;
B
Behdad Esfahbod 已提交
686 687
      const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
B
Behdad Esfahbod 已提交
688
      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
B
Behdad Esfahbod 已提交
689 690 691
    }
  }

692 693 694 695
  inline bool apply (hb_aat_apply_context_t *c) const
  {
    TRACE_APPLY (this);

696 697 698
    if (!c->plan->requested_kerning)
      return false;

699
    if (header.coverage & (header.CrossStream | header.Backwards))
700 701
      return false;

702
    accelerator_t accel (*this, c);
B
Behdad Esfahbod 已提交
703 704
    hb_kern_machine_t<accelerator_t> machine (accel);
    machine.kern (c->font, c->buffer, c->plan->kern_mask);
705 706 707 708

    return_trace (true);
  }

709 710
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
711
    TRACE_SANITIZE (this);
E
Ebrahim Byagowi 已提交
712
    return_trace (likely (c->check_struct (this) &&
B
Behdad Esfahbod 已提交
713 714 715 716 717 718 719 720 721
			  (is_long () ?
			   (
			     u.l.rowIndexTable.sanitize (c, this) &&
			     u.l.columnIndexTable.sanitize (c, this) &&
			     c->check_range (this, u.l.array)
			   ) : (
			     u.s.rowIndexTable.sanitize (c, this) &&
			     u.s.columnIndexTable.sanitize (c, this) &&
			     c->check_range (this, u.s.array)
722
			   )) &&
B
Behdad Esfahbod 已提交
723
			  (header.tuple_count () == 0 ||
724
			   c->check_range (this, vector))));
725 726
  }

B
Behdad Esfahbod 已提交
727 728 729
  struct accelerator_t
  {
    const KerxSubTableFormat6 &table;
730
    hb_aat_apply_context_t *c;
B
Behdad Esfahbod 已提交
731 732

    inline accelerator_t (const KerxSubTableFormat6 &table_,
733 734
			  hb_aat_apply_context_t *c_) :
			    table (table_), c (c_) {}
B
Behdad Esfahbod 已提交
735 736

    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
737
    { return table.get_kerning (left, right, c); }
B
Behdad Esfahbod 已提交
738 739
  };

740
  protected:
741
  KernSubTableHeader		header;
B
Behdad Esfahbod 已提交
742 743 744
  HBUINT32			flags;
  HBUINT16			rowCount;
  HBUINT16			columnCount;
B
Behdad Esfahbod 已提交
745
  union U
746
  {
B
Behdad Esfahbod 已提交
747
    struct Long
748
    {
749 750 751
      LOffsetTo<Lookup<HBUINT32>, false>	rowIndexTable;
      LOffsetTo<Lookup<HBUINT32>, false>	columnIndexTable;
      LOffsetTo<UnsizedArrayOf<FWORD32>, false>	array;
752
    } l;
B
Behdad Esfahbod 已提交
753 754
    struct Short
    {
755 756 757
      LOffsetTo<Lookup<HBUINT16>, false>	rowIndexTable;
      LOffsetTo<Lookup<HBUINT16>, false>	columnIndexTable;
      LOffsetTo<UnsizedArrayOf<FWORD>, false>	array;
B
Behdad Esfahbod 已提交
758
    } s;
759
  } u;
760
  LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
761
  public:
762
  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
763 764
};

B
Behdad Esfahbod 已提交
765 766 767

struct KerxSubTableHeader
{
B
Behdad Esfahbod 已提交
768
  typedef ExtendedTypes Types;
B
Behdad Esfahbod 已提交
769

B
Behdad Esfahbod 已提交
770
  inline unsigned int tuple_count (void) const { return tupleCount; }
771
  inline bool is_horizontal (void) const { return !(coverage & Vertical); }
B
Behdad Esfahbod 已提交
772

B
Behdad Esfahbod 已提交
773 774
  enum Coverage
  {
775 776 777 778 779 780 781 782 783 784
    Vertical	= 0x80000000u,	/* Set if table has vertical kerning values. */
    CrossStream	= 0x40000000u,	/* Set if table has cross-stream kerning values. */
    Variation	= 0x20000000u,	/* Set if table has variation kerning values. */
    Backwards	= 0x10000000u,	/* If clear, process the glyphs forwards, that
				 * is, from first to last in the glyph stream.
				 * If we, process them from last to first.
				 * This flag only applies to state-table based
				 * 'kerx' subtables (types 1 and 4). */
    Reserved	= 0x0FFFFF00u,	/* Reserved, set to zero. */
    SubtableType= 0x000000FFu,	/* Subtable type. */
B
Behdad Esfahbod 已提交
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
  };

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (likely (c->check_struct (this)));
  }

  public:
  HBUINT32	length;
  HBUINT32	coverage;
  HBUINT32	tupleCount;
  public:
  DEFINE_SIZE_STATIC (12);
};

B
Behdad Esfahbod 已提交
801
struct KerxSubTable
802
{
B
Behdad Esfahbod 已提交
803
  friend struct kerx;
B
Behdad Esfahbod 已提交
804

B
Behdad Esfahbod 已提交
805
  inline unsigned int get_size (void) const { return u.header.length; }
806
  inline unsigned int get_type (void) const { return u.header.coverage & u.header.SubtableType; }
807 808 809 810 811 812 813

  template <typename context_t>
  inline typename context_t::return_t dispatch (context_t *c) const
  {
    unsigned int subtable_type = get_type ();
    TRACE_DISPATCH (this, subtable_type);
    switch (subtable_type) {
B
Behdad Esfahbod 已提交
814 815 816 817 818 819
    case 0:	return_trace (c->dispatch (u.format0));
    case 1:	return_trace (c->dispatch (u.format1));
    case 2:	return_trace (c->dispatch (u.format2));
    case 4:	return_trace (c->dispatch (u.format4));
    case 6:	return_trace (c->dispatch (u.format6));
    default:	return_trace (c->default_return_value ());
820
    }
821 822
  }

823
  inline bool sanitize (hb_sanitize_context_t *c) const
824 825
  {
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
826 827
    if (!u.header.sanitize (c) ||
	!c->check_range (this, u.header.length))
828 829
      return_trace (false);

830
    return_trace (dispatch (c));
831 832
  }

833
  public:
834
  union {
835 836 837 838 839 840
  KerxSubTableHeader				header;
  KerxSubTableFormat0<KerxSubTableHeader>	format0;
  KerxSubTableFormat1<KerxSubTableHeader>	format1;
  KerxSubTableFormat2<KerxSubTableHeader>	format2;
  KerxSubTableFormat4<KerxSubTableHeader>	format4;
  KerxSubTableFormat6<KerxSubTableHeader>	format6;
841
  } u;
842
  public:
843
  DEFINE_SIZE_MIN (12);
844 845
};

846 847 848 849 850

/*
 * The 'kerx' Table
 */

851 852
template <typename T>
struct KerxTable
853
{
854 855
  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
  inline const T* thiz (void) const { return static_cast<const T *> (this); }
856

857 858 859
  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
  {
    typedef typename T::SubTable SubTable;
860

861 862 863 864 865 866 867 868 869 870 871 872 873
    int v = 0;
    const SubTable *st = &thiz()->firstSubTable;
    unsigned int count = thiz()->tableCount;
    for (unsigned int i = 0; i < count; i++)
    {
      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
	  !st->u.header.is_horizontal ())
        continue;
      v += st->get_kerning (left, right);
      st = &StructAfter<SubTable> (*st);
    }
    return v;
  }
874

875
  inline void apply (AAT::hb_aat_apply_context_t *c) const
876
  {
877 878
    typedef typename T::SubTable SubTable;

879
    bool seenCrossStream = false;
880
    c->set_lookup_index (0);
881 882
    const SubTable *st = &thiz()->firstSubTable;
    unsigned int count = thiz()->tableCount;
883 884
    for (unsigned int i = 0; i < count; i++)
    {
B
Behdad Esfahbod 已提交
885 886
      bool reverse;

887 888 889
      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
        goto skip;

890
      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
B
Behdad Esfahbod 已提交
891
	goto skip;
B
Behdad Esfahbod 已提交
892

893
      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
B
Behdad Esfahbod 已提交
894 895
		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);

896
      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
B
Behdad Esfahbod 已提交
897
	goto skip;
B
Behdad Esfahbod 已提交
898

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
      if (!seenCrossStream &&
	  (st->u.header.coverage & st->u.header.CrossStream))
      {
        /* Attach all glyphs into a chain. */
        seenCrossStream = true;
	hb_glyph_position_t *pos = c->buffer->pos;
	unsigned int count = c->buffer->len;
	for (unsigned int i = 0; i < count; i++)
	{
	  pos[i].attach_type() = ATTACH_TYPE_CURSIVE;
	  pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
	  /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
	   * since there needs to be a non-zero attachment for post-positioning to
	   * be needed. */
	}
      }

B
Behdad Esfahbod 已提交
916
      if (reverse)
B
Behdad Esfahbod 已提交
917
	c->buffer->reverse ();
B
Behdad Esfahbod 已提交
918

B
Behdad Esfahbod 已提交
919
      c->sanitizer.set_object (*st);
920

B
Behdad Esfahbod 已提交
921
      st->dispatch (c);
B
Behdad Esfahbod 已提交
922 923

      if (reverse)
B
Behdad Esfahbod 已提交
924
	c->buffer->reverse ();
B
Behdad Esfahbod 已提交
925

926
      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
B
Behdad Esfahbod 已提交
927 928

    skip:
929
      st = &StructAfter<SubTable> (*st);
B
Behdad Esfahbod 已提交
930
      c->set_lookup_index (c->lookup_index + 1);
931
    }
932 933
  }

934
  inline bool sanitize (hb_sanitize_context_t *c) const
935
  {
936
    TRACE_SANITIZE (this);
937 938 939
    if (unlikely (!thiz()->version.sanitize (c) ||
		  thiz()->version < T::minVersion ||
		  !thiz()->tableCount.sanitize (c)))
940
      return_trace (false);
941

942 943 944 945
    typedef typename T::SubTable SubTable;

    const SubTable *st = &thiz()->firstSubTable;
    unsigned int count = thiz()->tableCount;
946
    for (unsigned int i = 0; i < count; i++)
947
    {
948
      if (unlikely (!st->sanitize (c)))
949
	return_trace (false);
950
      st = &StructAfter<SubTable> (*st);
951 952 953 954
    }

    return_trace (true);
  }
955 956 957 958 959 960 961 962 963 964 965 966 967 968
};

struct kerx : KerxTable<kerx>
{
  friend struct KerxTable<kerx>;

  static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
  static const uint16_t minVersion = 2;

  typedef KerxSubTableHeader SubTableHeader;
  typedef SubTableHeader::Types Types;
  typedef KerxSubTable SubTable;

  inline bool has_data (void) const { return version; }
969 970

  protected:
971 972 973 974 975
  HBUINT16	version;	/* The version number of the extended kerning table
				 * (currently 2, 3, or 4). */
  HBUINT16	unused;		/* Set to 0. */
  HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
				 * table. */
976
  SubTable	firstSubTable;	/* Subtables. */
977 978
/*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */

979
  public:
980
  DEFINE_SIZE_MIN (8);
981 982
};

B
Behdad Esfahbod 已提交
983

984 985 986 987
} /* namespace AAT */


#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */