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