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