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