blob: ca2fdb493d167602ccffa211b01d47b718037123 [file] [log] [blame]
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03301/*
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03302 * Copyright © 2018 Ebrahim Byagowi
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +03303 * Copyright © 2018 Google, Inc.
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03304 *
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
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070031#include "hb-open-type.hh"
32#include "hb-aat-layout-common.hh"
Behdad Esfahbod1622ba52018-10-11 01:14:18 -040033#include "hb-ot-layout-gpos-table.hh"
Behdad Esfahboded2a4042018-10-07 22:33:41 -040034#include "hb-ot-kern-table.hh"
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033035
Ebrahim Byagowia02c3ee2018-04-12 13:38:19 +043036/*
37 * kerx -- Extended Kerning
38 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
39 */
Ebrahim Byagowi158f2812018-03-26 12:04:30 +043040#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x')
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +033041
42
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033043namespace AAT {
44
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +033045using namespace OT;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033046
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033047
Behdad Esfahbod29d87752018-10-19 16:06:54 -070048static inline int
49kerxTupleKern (int value,
50 unsigned int tupleCount,
51 const void *base,
52 hb_aat_apply_context_t *c)
53{
54 if (likely (!tupleCount)) return value;
55
56 unsigned int offset = value;
57 const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
58 if (unlikely (!pv->sanitize (&c->sanitizer))) return 0;
59 return *pv;
60}
61
62
Behdad Esfahbod5d341642018-10-10 18:14:41 -040063struct KerxSubTableHeader
64{
65 inline bool sanitize (hb_sanitize_context_t *c) const
66 {
67 TRACE_SANITIZE (this);
68 return_trace (likely (c->check_struct (this)));
69 }
70
71 public:
72 HBUINT32 length;
73 HBUINT32 coverage;
74 HBUINT32 tupleCount;
75 public:
76 DEFINE_SIZE_STATIC (12);
77};
78
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033079struct KerxSubTableFormat0
80{
Behdad Esfahbodb713c132018-10-20 14:56:28 -070081 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
82 hb_aat_apply_context_t *c) const
Behdad Esfahboded2a4042018-10-07 22:33:41 -040083 {
84 hb_glyph_pair_t pair = {left, right};
85 int i = pairs.bsearch (pair);
Behdad Esfahbodb713c132018-10-20 14:56:28 -070086 if (i == -1) return 0;
87 int v = pairs[i].get_kerning ();
88 return kerxTupleKern (v, header.tupleCount, this, c);
Behdad Esfahboded2a4042018-10-07 22:33:41 -040089 }
90
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -040091 inline bool apply (hb_aat_apply_context_t *c) const
92 {
93 TRACE_APPLY (this);
94
Behdad Esfahbod60f86d32018-10-10 18:10:05 -040095 if (!c->plan->requested_kerning)
96 return false;
97
Behdad Esfahbodb713c132018-10-20 14:56:28 -070098 accelerator_t accel (*this, c);
99 hb_kern_machine_t<accelerator_t> machine (accel);
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400100 machine.kern (c->font, c->buffer, c->plan->kern_mask);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400101
102 return_trace (true);
103 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330104
Behdad Esfahbodb713c132018-10-20 14:56:28 -0700105 struct accelerator_t
106 {
107 const KerxSubTableFormat0 &table;
108 hb_aat_apply_context_t *c;
109
110 inline accelerator_t (const KerxSubTableFormat0 &table_,
111 hb_aat_apply_context_t *c_) :
112 table (table_), c (c_) {}
113
114 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
115 { return table.get_kerning (left, right, c); }
116 };
117
118
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330119 inline bool sanitize (hb_sanitize_context_t *c) const
120 {
121 TRACE_SANITIZE (this);
Behdad Esfahbod68b70502018-10-20 12:09:41 -0700122 return_trace (likely (c->check_struct (this) &&
123 pairs.sanitize (c)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330124 }
125
126 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400127 KerxSubTableHeader header;
Behdad Esfahboded2a4042018-10-07 22:33:41 -0400128 BinSearchArrayOf<KernPair, HBUINT32>
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400129 pairs; /* Sorted kern records. */
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330130 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400131 DEFINE_SIZE_ARRAY (28, pairs);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330132};
133
134struct KerxSubTableFormat1
135{
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400136 struct EntryData
137 {
Behdad Esfahbod84967532018-10-10 21:18:37 -0400138 HBUINT16 kernActionIndex;/* Index into the kerning value array. If
139 * this index is 0xFFFF, then no kerning
140 * is to be performed. */
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400141 public:
142 DEFINE_SIZE_STATIC (2);
143 };
144
145 struct driver_context_t
146 {
147 static const bool in_place = true;
148 enum Flags
149 {
150 Push = 0x8000, /* If set, push this glyph on the kerning stack. */
151 DontAdvance = 0x4000, /* If set, don't advance to the next glyph
152 * before going to the new state. */
153 Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */
154 Reserved = 0x1FFF, /* Not used; set to 0. */
155 };
156
Behdad Esfahbod84967532018-10-10 21:18:37 -0400157 inline driver_context_t (const KerxSubTableFormat1 *table,
158 hb_aat_apply_context_t *c_) :
159 c (c_),
Behdad Esfahbod2b72c4b2018-10-10 21:53:14 -0400160 /* Apparently the offset kernAction is from the beginning of the state-machine,
161 * similar to offsets in morx table, NOT from beginning of this table, like
162 * other subtables in kerx. Discovered via testing. */
Behdad Esfahbod9f450f02018-10-10 21:46:58 -0400163 kernAction (&table->machine + table->kernAction),
Behdad Esfahbod84967532018-10-10 21:18:37 -0400164 depth (0) {}
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400165
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330166 inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400167 const Entry<EntryData> *entry)
168 {
Behdad Esfahbod84967532018-10-10 21:18:37 -0400169 return entry->data.kernActionIndex != 0xFFFF;
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400170 }
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330171 inline bool transition (StateTableDriver<MorxTypes, EntryData> *driver,
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400172 const Entry<EntryData> *entry)
173 {
Behdad Esfahbod84967532018-10-10 21:18:37 -0400174 hb_buffer_t *buffer = driver->buffer;
175 unsigned int flags = entry->flags;
176
177 if (flags & Reset)
178 {
Behdad Esfahbod2352cc32018-10-19 09:58:45 -0700179 depth = 0;
Behdad Esfahbod84967532018-10-10 21:18:37 -0400180 }
181
182 if (flags & Push)
183 {
Behdad Esfahbod2352cc32018-10-19 09:58:45 -0700184 if (likely (depth < ARRAY_LENGTH (stack)))
Behdad Esfahbod84967532018-10-10 21:18:37 -0400185 stack[depth++] = buffer->idx;
186 else
187 depth = 0; /* Probably not what CoreText does, but better? */
188 }
189
190 if (entry->data.kernActionIndex != 0xFFFF)
191 {
192 const FWORD *actions = &kernAction[entry->data.kernActionIndex];
Behdad Esfahbod2352cc32018-10-19 09:58:45 -0700193 if (!c->sanitizer.check_array (actions, depth))
Behdad Esfahbod84967532018-10-10 21:18:37 -0400194 {
195 depth = 0;
196 return false;
197 }
198
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400199 hb_mask_t kern_mask = c->plan->kern_mask;
Behdad Esfahbod2352cc32018-10-19 09:58:45 -0700200 for (unsigned int i = 0; i < depth; i++)
Behdad Esfahbod84967532018-10-10 21:18:37 -0400201 {
Behdad Esfahbod9f450f02018-10-10 21:46:58 -0400202 /* Apparently, when spec says "Each pops one glyph from the kerning stack
203 * and applies the kerning value to it.", it doesn't mean it in that order.
204 * The deepest item in the stack corresponds to the first item in the action
205 * list. Discovered by testing. */
206 unsigned int idx = stack[i];
Behdad Esfahbod84967532018-10-10 21:18:37 -0400207 int v = *actions++;
Behdad Esfahbodbb357252018-10-15 10:20:39 -0700208 if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400209 {
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400210 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
Behdad Esfahbod8034d1d2018-11-02 14:47:42 -0400211 {
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400212 buffer->pos[idx].x_advance += c->font->em_scale_x (v);
Behdad Esfahbod8034d1d2018-11-02 14:47:42 -0400213 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
214 buffer->pos[idx].x_offset += c->font->em_scale_x (v);
215 }
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400216 else
Behdad Esfahbod8034d1d2018-11-02 14:47:42 -0400217 {
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400218 buffer->pos[idx].y_advance += c->font->em_scale_y (v);
Behdad Esfahbod8034d1d2018-11-02 14:47:42 -0400219 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
220 buffer->pos[idx].y_offset += c->font->em_scale_y (v);
221 }
Behdad Esfahbodf7c45bc2018-10-10 22:15:13 -0400222 }
Behdad Esfahbod84967532018-10-10 21:18:37 -0400223 }
Behdad Esfahbod9f450f02018-10-10 21:46:58 -0400224 depth = 0;
Behdad Esfahbod84967532018-10-10 21:18:37 -0400225 }
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400226
227 return true;
228 }
229
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400230 private:
Behdad Esfahbod84967532018-10-10 21:18:37 -0400231 hb_aat_apply_context_t *c;
232 const UnsizedArrayOf<FWORD> &kernAction;
233 unsigned int stack[8];
234 unsigned int depth;
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400235 };
236
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400237 inline bool apply (hb_aat_apply_context_t *c) const
238 {
239 TRACE_APPLY (this);
240
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400241 if (!c->plan->requested_kerning)
242 return false;
243
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700244 if (header.tupleCount)
245 return_trace (false); /* TODO kerxTupleKern */
246
Behdad Esfahbod84967532018-10-10 21:18:37 -0400247 driver_context_t dc (this, c);
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400248
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330249 StateTableDriver<MorxTypes, EntryData> driver (machine, c->buffer, c->font->face);
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400250 driver.drive (&dc);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400251
252 return_trace (true);
253 }
254
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330255 inline bool sanitize (hb_sanitize_context_t *c) const
256 {
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330257 TRACE_SANITIZE (this);
Behdad Esfahbod40f2b932018-10-14 14:56:32 -0700258 /* The rest of array sanitizations are done at run-time. */
259 return_trace (likely (c->check_struct (this) &&
260 machine.sanitize (c)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330261 }
262
263 protected:
Behdad Esfahbod339036d2018-10-10 20:37:22 -0400264 KerxSubTableHeader header;
Behdad Esfahbod8034d1d2018-11-02 14:47:42 -0400265 StateTable<MorxTypes, EntryData> machine;
Behdad Esfahbod84967532018-10-10 21:18:37 -0400266 LOffsetTo<UnsizedArrayOf<FWORD>, false> kernAction;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330267 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400268 DEFINE_SIZE_STATIC (32);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330269};
270
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330271struct KerxSubTableFormat2
272{
Behdad Esfahbod1a5a3322018-10-07 23:08:39 -0400273 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
Behdad Esfahbod57331132018-10-13 12:16:12 -0400274 hb_aat_apply_context_t *c) const
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330275 {
Behdad Esfahbod57331132018-10-13 12:16:12 -0400276 unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
Behdad Esfahbod7727e732018-10-10 13:24:51 -0400277 unsigned int l = (this+leftClassTable).get_value_or_null (left, num_glyphs);
278 unsigned int r = (this+rightClassTable).get_value_or_null (right, num_glyphs);
Behdad Esfahbod8aa83d92018-10-07 22:43:59 -0400279 unsigned int offset = l + r;
Behdad Esfahbod71f76f22018-10-13 13:36:27 -0400280 const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
281 if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700282 return kerxTupleKern (*v, header.tupleCount, this, c);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330283 }
284
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400285 inline bool apply (hb_aat_apply_context_t *c) const
286 {
287 TRACE_APPLY (this);
288
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400289 if (!c->plan->requested_kerning)
290 return false;
291
Behdad Esfahbod57331132018-10-13 12:16:12 -0400292 accelerator_t accel (*this, c);
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400293 hb_kern_machine_t<accelerator_t> machine (accel);
294 machine.kern (c->font, c->buffer, c->plan->kern_mask);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400295
296 return_trace (true);
297 }
298
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400299 struct accelerator_t
300 {
301 const KerxSubTableFormat2 &table;
Behdad Esfahbod57331132018-10-13 12:16:12 -0400302 hb_aat_apply_context_t *c;
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400303
304 inline accelerator_t (const KerxSubTableFormat2 &table_,
Behdad Esfahbod57331132018-10-13 12:16:12 -0400305 hb_aat_apply_context_t *c_) :
306 table (table_), c (c_) {}
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400307
308 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
Behdad Esfahbod57331132018-10-13 12:16:12 -0400309 { return table.get_kerning (left, right, c); }
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400310 };
311
Behdad Esfahbodb713c132018-10-20 14:56:28 -0700312 inline bool sanitize (hb_sanitize_context_t *c) const
313 {
314 TRACE_SANITIZE (this);
315 return_trace (likely (c->check_struct (this) &&
316 leftClassTable.sanitize (c, this) &&
317 rightClassTable.sanitize (c, this) &&
318 c->check_range (this, array)));
319 }
320
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330321 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400322 KerxSubTableHeader header;
323 HBUINT32 rowWidth; /* The width, in bytes, of a row in the table. */
Behdad Esfahbod87205ef2018-10-16 15:40:44 -0700324 LOffsetTo<Lookup<HBUINT16>, false>
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400325 leftClassTable; /* Offset from beginning of this subtable to
326 * left-hand class table. */
Behdad Esfahbod87205ef2018-10-16 15:40:44 -0700327 LOffsetTo<Lookup<HBUINT16>, false>
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400328 rightClassTable;/* Offset from beginning of this subtable to
329 * right-hand class table. */
Behdad Esfahbodc9165f52018-10-10 20:43:21 -0400330 LOffsetTo<UnsizedArrayOf<FWORD>, false>
331 array; /* Offset from beginning of this subtable to
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400332 * the start of the kerning array. */
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330333 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400334 DEFINE_SIZE_STATIC (28);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330335};
336
337struct KerxSubTableFormat4
338{
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400339 struct EntryData
340 {
341 HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
342 * the action to perform. */
343 public:
344 DEFINE_SIZE_STATIC (2);
345 };
346
347 struct driver_context_t
348 {
349 static const bool in_place = true;
350 enum Flags
351 {
352 Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */
353 DontAdvance = 0x4000, /* If set, don't advance to the next glyph before
354 * going to the new state. */
355 Reserved = 0x3FFF, /* Not used; set to 0. */
356 };
357
358 enum SubTableFlags
359 {
360 ActionType = 0xC0000000, /* A two-bit field containing the action type. */
361 Unused = 0x3F000000, /* Unused - must be zero. */
362 Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning
363 * of the subtable to the beginning of the control
364 * point table. */
365 };
366
367 inline driver_context_t (const KerxSubTableFormat4 *table,
368 hb_aat_apply_context_t *c_) :
369 c (c_),
370 action_type ((table->flags & ActionType) >> 30),
371 ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
372 mark_set (false),
373 mark (0) {}
374
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330375 inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400376 const Entry<EntryData> *entry)
377 {
378 return entry->data.ankrActionIndex != 0xFFFF;
379 }
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330380 inline bool transition (StateTableDriver<MorxTypes, EntryData> *driver,
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400381 const Entry<EntryData> *entry)
382 {
383 hb_buffer_t *buffer = driver->buffer;
384 unsigned int flags = entry->flags;
385
Behdad Esfahbodbb357252018-10-15 10:20:39 -0700386 if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400387 {
Behdad Esfahbodb6bc0d42018-10-11 01:17:57 -0400388 hb_glyph_position_t &o = buffer->cur_pos();
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400389 switch (action_type)
390 {
391 case 0: /* Control Point Actions.*/
392 {
393 /* indexed into glyph outline. */
394 const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
395 if (!c->sanitizer.check_array (data, 2))
396 return false;
397 HB_UNUSED unsigned int markControlPoint = *data++;
398 HB_UNUSED unsigned int currControlPoint = *data++;
Behdad Esfahbodfbbd9262018-10-11 01:22:29 -0400399 hb_position_t markX = 0;
400 hb_position_t markY = 0;
401 hb_position_t currX = 0;
402 hb_position_t currY = 0;
403 if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
404 markControlPoint,
405 HB_DIRECTION_LTR /*XXX*/,
406 &markX, &markY) ||
407 !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
408 currControlPoint,
409 HB_DIRECTION_LTR /*XXX*/,
410 &currX, &currY))
411 return true; /* True, such that the machine continues. */
412
413 o.x_offset = markX - currX;
414 o.y_offset = markY - currY;
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400415 }
416 break;
417
418 case 1: /* Anchor Point Actions. */
419 {
420 /* Indexed into 'ankr' table. */
421 const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
422 if (!c->sanitizer.check_array (data, 2))
423 return false;
Behdad Esfahbod1622ba52018-10-11 01:14:18 -0400424 unsigned int markAnchorPoint = *data++;
425 unsigned int currAnchorPoint = *data++;
Behdad Esfahbodb605db22018-11-04 12:58:02 -0500426 const Anchor markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
427 markAnchorPoint,
428 c->sanitizer.get_num_glyphs (),
429 c->ankr_end);
430 const Anchor currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
431 currAnchorPoint,
432 c->sanitizer.get_num_glyphs (),
433 c->ankr_end);
Behdad Esfahbodb6bc0d42018-10-11 01:17:57 -0400434
Behdad Esfahbod1622ba52018-10-11 01:14:18 -0400435 o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
436 o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400437 }
438 break;
439
440 case 2: /* Control Point Coordinate Actions. */
441 {
442 const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
443 if (!c->sanitizer.check_array (data, 4))
444 return false;
Behdad Esfahbodb6bc0d42018-10-11 01:17:57 -0400445 int markX = *data++;
446 int markY = *data++;
447 int currX = *data++;
448 int currY = *data++;
449
450 o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
451 o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400452 }
453 break;
454 }
Behdad Esfahbodb6bc0d42018-10-11 01:17:57 -0400455 o.attach_type() = ATTACH_TYPE_MARK;
456 o.attach_chain() = (int) mark - (int) buffer->idx;
457 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400458 }
459
460 if (flags & Mark)
461 {
462 mark_set = true;
463 mark = buffer->idx;
464 }
465
466 return true;
467 }
468
469 private:
470 hb_aat_apply_context_t *c;
471 unsigned int action_type;
472 const HBUINT16 *ankrData;
473 bool mark_set;
474 unsigned int mark;
475 };
476
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400477 inline bool apply (hb_aat_apply_context_t *c) const
478 {
479 TRACE_APPLY (this);
480
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400481 driver_context_t dc (this, c);
482
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330483 StateTableDriver<MorxTypes, EntryData> driver (machine, c->buffer, c->font->face);
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400484 driver.drive (&dc);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400485
486 return_trace (true);
487 }
488
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330489 inline bool sanitize (hb_sanitize_context_t *c) const
490 {
491 TRACE_SANITIZE (this);
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400492 /* The rest of array sanitizations are done at run-time. */
Behdad Esfahbod40f2b932018-10-14 14:56:32 -0700493 return_trace (likely (c->check_struct (this) &&
494 machine.sanitize (c)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330495 }
496
497 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400498 KerxSubTableHeader header;
Ebrahim Byagowib053cab2018-10-30 18:41:34 +0330499 StateTable<MorxTypes, EntryData>
500 machine;
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400501 HBUINT32 flags;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330502 public:
Behdad Esfahbod28f03672018-10-11 00:12:49 -0400503 DEFINE_SIZE_STATIC (32);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330504};
505
506struct KerxSubTableFormat6
507{
Behdad Esfahbod22955b22018-10-10 19:58:20 -0400508 enum Flags
509 {
510 ValuesAreLong = 0x00000001,
511 };
512
513 inline bool is_long (void) const { return flags & ValuesAreLong; }
514
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400515 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
Behdad Esfahbod57331132018-10-13 12:16:12 -0400516 hb_aat_apply_context_t *c) const
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400517 {
Behdad Esfahbod57331132018-10-13 12:16:12 -0400518 unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400519 if (is_long ())
520 {
521 const U::Long &t = u.l;
522 unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
523 unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
524 unsigned int offset = l + r;
Behdad Esfahbodfc45e692018-10-13 11:39:12 -0400525 if (unlikely (offset < l)) return 0; /* Addition overflow. */
526 if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
Behdad Esfahbod71f76f22018-10-13 13:36:27 -0400527 const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
528 if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700529 return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400530 }
531 else
532 {
533 const U::Short &t = u.s;
534 unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
535 unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
536 unsigned int offset = l + r;
Behdad Esfahbod71f76f22018-10-13 13:36:27 -0400537 const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
538 if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700539 return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400540 }
541 }
542
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400543 inline bool apply (hb_aat_apply_context_t *c) const
544 {
545 TRACE_APPLY (this);
546
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400547 if (!c->plan->requested_kerning)
548 return false;
549
Behdad Esfahbod57331132018-10-13 12:16:12 -0400550 accelerator_t accel (*this, c);
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400551 hb_kern_machine_t<accelerator_t> machine (accel);
552 machine.kern (c->font, c->buffer, c->plan->kern_mask);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400553
554 return_trace (true);
555 }
556
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330557 inline bool sanitize (hb_sanitize_context_t *c) const
558 {
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330559 TRACE_SANITIZE (this);
Ebrahim Byagowi2a4cdfa2018-04-20 21:12:58 +0430560 return_trace (likely (c->check_struct (this) &&
Behdad Esfahbod11703af2018-10-15 21:16:58 -0700561 (is_long () ?
562 (
563 u.l.rowIndexTable.sanitize (c, this) &&
564 u.l.columnIndexTable.sanitize (c, this) &&
565 c->check_range (this, u.l.array)
566 ) : (
567 u.s.rowIndexTable.sanitize (c, this) &&
568 u.s.columnIndexTable.sanitize (c, this) &&
569 c->check_range (this, u.s.array)
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700570 )) &&
571 (header.tupleCount == 0 ||
572 c->check_range (this, vector))));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330573 }
574
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400575 struct accelerator_t
576 {
577 const KerxSubTableFormat6 &table;
Behdad Esfahbod57331132018-10-13 12:16:12 -0400578 hb_aat_apply_context_t *c;
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400579
580 inline accelerator_t (const KerxSubTableFormat6 &table_,
Behdad Esfahbod57331132018-10-13 12:16:12 -0400581 hb_aat_apply_context_t *c_) :
582 table (table_), c (c_) {}
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400583
584 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
Behdad Esfahbod57331132018-10-13 12:16:12 -0400585 { return table.get_kerning (left, right, c); }
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400586 };
587
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330588 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400589 KerxSubTableHeader header;
590 HBUINT32 flags;
591 HBUINT16 rowCount;
592 HBUINT16 columnCount;
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400593 union U
Behdad Esfahbod22955b22018-10-10 19:58:20 -0400594 {
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400595 struct Long
Behdad Esfahbod22955b22018-10-10 19:58:20 -0400596 {
Behdad Esfahbod87205ef2018-10-16 15:40:44 -0700597 LOffsetTo<Lookup<HBUINT32>, false> rowIndexTable;
598 LOffsetTo<Lookup<HBUINT32>, false> columnIndexTable;
599 LOffsetTo<UnsizedArrayOf<FWORD32>, false> array;
Behdad Esfahbod22955b22018-10-10 19:58:20 -0400600 } l;
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400601 struct Short
602 {
Behdad Esfahbod87205ef2018-10-16 15:40:44 -0700603 LOffsetTo<Lookup<HBUINT16>, false> rowIndexTable;
604 LOffsetTo<Lookup<HBUINT16>, false> columnIndexTable;
605 LOffsetTo<UnsizedArrayOf<FWORD>, false> array;
Behdad Esfahbodab1f30b2018-10-10 20:10:20 -0400606 } s;
Behdad Esfahbod22955b22018-10-10 19:58:20 -0400607 } u;
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700608 LOffsetTo<UnsizedArrayOf<FWORD>, false> vector;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330609 public:
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700610 DEFINE_SIZE_STATIC (36);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330611};
612
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330613struct KerxTable
614{
Behdad Esfahbode640f3a2018-10-09 08:28:07 -0400615 friend struct kerx;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400616
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400617 inline unsigned int get_size (void) const { return u.header.length; }
618 inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400619
620 enum Coverage
621 {
622 Vertical = 0x80000000, /* Set if table has vertical kerning values. */
623 CrossStream = 0x40000000, /* Set if table has cross-stream kerning values. */
624 Variation = 0x20000000, /* Set if table has variation kerning values. */
Behdad Esfahbod362d3242018-10-09 23:27:00 -0400625 Backwards = 0x10000000, /* If clear, process the glyphs forwards, that
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400626 * is, from first to last in the glyph stream.
627 * If we, process them from last to first.
628 * This flag only applies to state-table based
629 * 'kerx' subtables (types 1 and 4). */
630 Reserved = 0x0FFFFF00, /* Reserved, set to zero. */
631 SubtableType = 0x000000FF, /* Subtable type. */
632 };
633
634 template <typename context_t>
635 inline typename context_t::return_t dispatch (context_t *c) const
636 {
637 unsigned int subtable_type = get_type ();
638 TRACE_DISPATCH (this, subtable_type);
639 switch (subtable_type) {
Behdad Esfahbodbfafe202018-11-06 12:11:45 -0500640 case 0: return_trace (c->dispatch (u.format0));
641 case 1: return_trace (c->dispatch (u.format1));
642 case 2: return_trace (c->dispatch (u.format2));
643 case 4: return_trace (c->dispatch (u.format4));
644 case 6: return_trace (c->dispatch (u.format6));
645 default: return_trace (c->default_return_value ());
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400646 }
647 }
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330648
649 inline bool sanitize (hb_sanitize_context_t *c) const
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330650 {
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330651 TRACE_SANITIZE (this);
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400652 if (!u.header.sanitize (c) ||
653 !c->check_range (this, u.header.length))
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330654 return_trace (false);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330655
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400656 return_trace (dispatch (c));
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330657 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330658
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330659protected:
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330660 union {
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400661 KerxSubTableHeader header;
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330662 KerxSubTableFormat0 format0;
663 KerxSubTableFormat1 format1;
664 KerxSubTableFormat2 format2;
665 KerxSubTableFormat4 format4;
666 KerxSubTableFormat6 format6;
667 } u;
668public:
669 DEFINE_SIZE_MIN (12);
670};
671
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400672
673/*
674 * The 'kerx' Table
675 */
676
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330677struct kerx
678{
Ebrahim Byagowi158f2812018-03-26 12:04:30 +0430679 static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330680
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400681 inline bool has_data (void) const { return version != 0; }
682
683 inline void apply (hb_aat_apply_context_t *c) const
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330684 {
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400685 c->set_lookup_index (0);
686 const KerxTable *table = &firstTable;
687 unsigned int count = tableCount;
688 for (unsigned int i = 0; i < count; i++)
689 {
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400690 bool reverse;
691
Behdad Esfahbod29d87752018-10-19 16:06:54 -0700692 if (table->u.header.coverage & (KerxTable::CrossStream))
693 goto skip; /* We do NOT handle cross-stream. */
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400694
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400695 if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400696 bool (table->u.header.coverage & KerxTable::Vertical))
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400697 goto skip;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400698
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400699 reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400700 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
701
702 if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400703 goto skip;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400704
705 if (reverse)
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400706 c->buffer->reverse ();
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400707
Behdad Esfahbod948f59a2018-10-09 23:07:47 -0400708 c->sanitizer.set_object (*table);
709
Behdad Esfahbodad763072018-10-09 22:57:00 -0400710 /* XXX Reverse-kern is not working yet...
711 * hb_kern_machine_t would need to know that it's reverse-kerning.
712 * Or better yet, make it work in reverse as well, so we don't have
713 * to reverse and reverse back? */
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400714 table->dispatch (c);
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400715
716 if (reverse)
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400717 c->buffer->reverse ();
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400718
719 (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
720
721 skip:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400722 table = &StructAfter<KerxTable> (*table);
723 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330724 }
725
726 inline bool sanitize (hb_sanitize_context_t *c) const
727 {
728 TRACE_SANITIZE (this);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400729 if (!version.sanitize (c) || version < 2 ||
730 !tableCount.sanitize (c))
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330731 return_trace (false);
732
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400733 const KerxTable *table = &firstTable;
734 unsigned int count = tableCount;
735 for (unsigned int i = 0; i < count; i++)
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330736 {
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400737 if (!table->sanitize (c))
738 return_trace (false);
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330739 table = &StructAfter<KerxTable> (*table);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330740 }
741
742 return_trace (true);
743 }
744
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330745 protected:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400746 HBUINT16 version; /* The version number of the extended kerning table
747 * (currently 2, 3, or 4). */
748 HBUINT16 unused; /* Set to 0. */
749 HBUINT32 tableCount; /* The number of subtables included in the extended kerning
750 * table. */
751 KerxTable firstTable; /* Subtables. */
752/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */
753
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330754 public:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400755 DEFINE_SIZE_MIN (8);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330756};
757
758} /* namespace AAT */
759
760
761#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */