2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
30 #define HB_OT_LAYOUT_GPOS_PRIVATE_HH
32 #include "hb-ot-layout-gsubgpos-private.hh"
37 /* buffer var allocations */
38 #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
39 #define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */
42 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
46 typedef Value ValueRecord[VAR];
48 struct ValueFormat : USHORT
52 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
53 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
54 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
55 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
56 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
57 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
58 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
59 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
60 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
61 reserved = 0xF000, /* For future use */
63 devices = 0x00F0 /* Mask for having any Device table */
66 /* All fields are options. Only those available advance the value pointer. */
68 SHORT xPlacement; /* Horizontal adjustment for
69 * placement--in design units */
70 SHORT yPlacement; /* Vertical adjustment for
71 * placement--in design units */
72 SHORT xAdvance; /* Horizontal adjustment for
73 * advance--in design units (only used
74 * for horizontal writing) */
75 SHORT yAdvance; /* Vertical adjustment for advance--in
76 * design units (only used for vertical
78 Offset xPlaDevice; /* Offset to Device table for
79 * horizontal placement--measured from
80 * beginning of PosTable (may be NULL) */
81 Offset yPlaDevice; /* Offset to Device table for vertical
82 * placement--measured from beginning
83 * of PosTable (may be NULL) */
84 Offset xAdvDevice; /* Offset to Device table for
85 * horizontal advance--measured from
86 * beginning of PosTable (may be NULL) */
87 Offset yAdvDevice; /* Offset to Device table for vertical
88 * advance--measured from beginning of
89 * PosTable (may be NULL) */
92 inline unsigned int get_len (void) const
93 { return _hb_popcount32 ((unsigned int) *this); }
94 inline unsigned int get_size (void) const
95 { return get_len () * Value::static_size; }
97 void apply_value (hb_font_t *font,
100 hb_glyph_position_t &glyph_pos) const
102 unsigned int x_ppem, y_ppem;
103 unsigned int format = *this;
107 /* design units -> fractional pixel */
108 if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++));
109 if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++));
110 if (format & xAdvance) glyph_pos.x_advance += font->em_scale_x (get_short (values++));
111 if (format & yAdvance) glyph_pos.y_advance += font->em_scale_y (get_short (values++));
113 if (!has_device ()) return;
115 x_ppem = font->x_ppem;
116 y_ppem = font->y_ppem;
118 if (!x_ppem && !y_ppem) return;
120 /* pixel -> fractional pixel */
121 if (format & xPlaDevice) {
122 if (x_ppem) glyph_pos.x_offset += (base + get_device (values++)).get_x_delta (font); else values++;
124 if (format & yPlaDevice) {
125 if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_y_delta (font); else values++;
127 if (format & xAdvDevice) {
128 if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (font); else values++;
130 if (format & yAdvDevice) {
131 if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_y_delta (font); else values++;
136 inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
137 unsigned int format = *this;
139 if (format & xPlacement) values++;
140 if (format & yPlacement) values++;
141 if (format & xAdvance) values++;
142 if (format & yAdvance) values++;
144 if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
145 if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
146 if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
147 if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
152 static inline OffsetTo<Device>& get_device (Value* value)
153 { return *CastP<OffsetTo<Device> > (value); }
154 static inline const OffsetTo<Device>& get_device (const Value* value)
155 { return *CastP<OffsetTo<Device> > (value); }
157 static inline const SHORT& get_short (const Value* value)
158 { return *CastP<SHORT> (value); }
162 inline bool has_device (void) const {
163 unsigned int format = *this;
164 return (format & devices) != 0;
167 inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
169 return c->check_range (values, get_size ())
170 && (!has_device () || sanitize_value_devices (c, base, values));
173 inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
175 unsigned int len = get_len ();
177 if (!c->check_array (values, get_size (), count)) return false;
179 if (!has_device ()) return true;
181 for (unsigned int i = 0; i < count; i++) {
182 if (!sanitize_value_devices (c, base, values))
190 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
191 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
194 if (!has_device ()) return true;
196 for (unsigned int i = 0; i < count; i++) {
197 if (!sanitize_value_devices (c, base, values))
209 friend struct Anchor;
212 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
213 hb_direction_t direction HB_UNUSED,
214 hb_position_t *x, hb_position_t *y) const
216 *x = font->em_scale_x (xCoordinate);
217 *y = font->em_scale_y (yCoordinate);
220 inline bool sanitize (hb_sanitize_context_t *c) {
222 return c->check_struct (this);
226 USHORT format; /* Format identifier--format = 1 */
227 SHORT xCoordinate; /* Horizontal value--in design units */
228 SHORT yCoordinate; /* Vertical value--in design units */
230 DEFINE_SIZE_STATIC (6);
235 friend struct Anchor;
238 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
239 hb_direction_t direction,
240 hb_position_t *x, hb_position_t *y) const
242 unsigned int x_ppem = font->x_ppem;
243 unsigned int y_ppem = font->y_ppem;
244 hb_position_t cx, cy;
245 hb_bool_t ret = false;
247 if (x_ppem || y_ppem)
248 ret = hb_font_get_contour_point_for_direction (font, glyph_id, anchorPoint, direction, &cx, &cy);
249 *x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate);
250 *y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate);
253 inline bool sanitize (hb_sanitize_context_t *c) {
255 return c->check_struct (this);
259 USHORT format; /* Format identifier--format = 2 */
260 SHORT xCoordinate; /* Horizontal value--in design units */
261 SHORT yCoordinate; /* Vertical value--in design units */
262 USHORT anchorPoint; /* Index to glyph contour point */
264 DEFINE_SIZE_STATIC (8);
269 friend struct Anchor;
272 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
273 hb_direction_t direction HB_UNUSED,
274 hb_position_t *x, hb_position_t *y) const
276 *x = font->em_scale_x (xCoordinate);
277 *y = font->em_scale_y (yCoordinate);
279 /* pixel -> fractional pixel */
281 *x += (this+xDeviceTable).get_x_delta (font);
283 *y += (this+yDeviceTable).get_x_delta (font);
286 inline bool sanitize (hb_sanitize_context_t *c) {
288 return c->check_struct (this)
289 && xDeviceTable.sanitize (c, this)
290 && yDeviceTable.sanitize (c, this);
294 USHORT format; /* Format identifier--format = 3 */
295 SHORT xCoordinate; /* Horizontal value--in design units */
296 SHORT yCoordinate; /* Vertical value--in design units */
298 xDeviceTable; /* Offset to Device table for X
299 * coordinate-- from beginning of
300 * Anchor table (may be NULL) */
302 yDeviceTable; /* Offset to Device table for Y
303 * coordinate-- from beginning of
304 * Anchor table (may be NULL) */
306 DEFINE_SIZE_STATIC (10);
311 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
312 hb_direction_t direction,
313 hb_position_t *x, hb_position_t *y) const
317 case 1: u.format1.get_anchor (font, glyph_id, direction, x, y); return;
318 case 2: u.format2.get_anchor (font, glyph_id, direction, x, y); return;
319 case 3: u.format3.get_anchor (font, glyph_id, direction, x, y); return;
324 inline bool sanitize (hb_sanitize_context_t *c) {
326 if (!u.format.sanitize (c)) return false;
328 case 1: return u.format1.sanitize (c);
329 case 2: return u.format2.sanitize (c);
330 case 3: return u.format3.sanitize (c);
337 USHORT format; /* Format identifier */
338 AnchorFormat1 format1;
339 AnchorFormat2 format2;
340 AnchorFormat3 format3;
343 DEFINE_SIZE_UNION (2, format);
349 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
350 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
351 return this+matrix[row * cols + col];
354 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
356 if (!c->check_struct (this)) return false;
357 if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false;
358 unsigned int count = rows * cols;
359 if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
360 for (unsigned int i = 0; i < count; i++)
361 if (!matrix[i].sanitize (c, this)) return false;
365 USHORT rows; /* Number of rows */
368 matrix[VAR]; /* Matrix of offsets to Anchor tables--
369 * from beginning of AnchorMatrix table */
371 DEFINE_SIZE_ARRAY (2, matrix);
377 friend struct MarkArray;
379 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
381 return c->check_struct (this)
382 && markAnchor.sanitize (c, base);
386 USHORT klass; /* Class defined for this mark */
388 markAnchor; /* Offset to Anchor table--from
389 * beginning of MarkArray table */
391 DEFINE_SIZE_STATIC (4);
394 struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */
396 inline bool apply (hb_apply_context_t *c,
397 unsigned int mark_index, unsigned int glyph_index,
398 const AnchorMatrix &anchors, unsigned int class_count,
399 unsigned int glyph_pos) const
402 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
403 unsigned int mark_class = record.klass;
405 const Anchor& mark_anchor = this + record.markAnchor;
406 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
408 hb_position_t mark_x, mark_y, base_x, base_y;
410 mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->i].codepoint, c->direction, &mark_x, &mark_y);
411 glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, c->direction, &base_x, &base_y);
413 hb_glyph_position_t &o = c->buffer->pos[c->buffer->i];
414 o.x_offset = base_x - mark_x;
415 o.y_offset = base_y - mark_y;
416 o.attach_lookback() = c->buffer->i - glyph_pos;
422 inline bool sanitize (hb_sanitize_context_t *c) {
424 return ArrayOf<MarkRecord>::sanitize (c, this);
431 struct SinglePosFormat1
433 friend struct SinglePos;
436 inline bool apply (hb_apply_context_t *c) const
439 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
440 if (likely (index == NOT_COVERED))
443 valueFormat.apply_value (c->font, this, values, c->buffer->pos[c->buffer->i]);
449 inline bool sanitize (hb_sanitize_context_t *c) {
451 return c->check_struct (this)
452 && coverage.sanitize (c, this)
453 && valueFormat.sanitize_value (c, this, values);
457 USHORT format; /* Format identifier--format = 1 */
459 coverage; /* Offset to Coverage table--from
460 * beginning of subtable */
461 ValueFormat valueFormat; /* Defines the types of data in the
463 ValueRecord values; /* Defines positioning
464 * value(s)--applied to all glyphs in
465 * the Coverage table */
467 DEFINE_SIZE_ARRAY (6, values);
470 struct SinglePosFormat2
472 friend struct SinglePos;
475 inline bool apply (hb_apply_context_t *c) const
478 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
479 if (likely (index == NOT_COVERED))
482 if (likely (index >= valueCount))
485 valueFormat.apply_value (c->font, this,
486 &values[index * valueFormat.get_len ()],
487 c->buffer->pos[c->buffer->i]);
493 inline bool sanitize (hb_sanitize_context_t *c) {
495 return c->check_struct (this)
496 && coverage.sanitize (c, this)
497 && valueFormat.sanitize_values (c, this, values, valueCount);
501 USHORT format; /* Format identifier--format = 2 */
503 coverage; /* Offset to Coverage table--from
504 * beginning of subtable */
505 ValueFormat valueFormat; /* Defines the types of data in the
507 USHORT valueCount; /* Number of ValueRecords */
508 ValueRecord values; /* Array of ValueRecords--positioning
509 * values applied to glyphs */
511 DEFINE_SIZE_ARRAY (8, values);
516 friend struct PosLookupSubTable;
519 inline bool apply (hb_apply_context_t *c) const
523 case 1: return u.format1.apply (c);
524 case 2: return u.format2.apply (c);
525 default:return false;
529 inline bool sanitize (hb_sanitize_context_t *c) {
531 if (!u.format.sanitize (c)) return false;
533 case 1: return u.format1.sanitize (c);
534 case 2: return u.format2.sanitize (c);
541 USHORT format; /* Format identifier */
542 SinglePosFormat1 format1;
543 SinglePosFormat2 format2;
548 struct PairValueRecord
550 friend struct PairSet;
553 GlyphID secondGlyph; /* GlyphID of second glyph in the
554 * pair--first glyph is listed in the
556 ValueRecord values; /* Positioning data for the first glyph
557 * followed by for second glyph */
559 DEFINE_SIZE_ARRAY (2, values);
564 friend struct PairPosFormat1;
566 inline bool apply (hb_apply_context_t *c,
567 const ValueFormat *valueFormats,
568 unsigned int pos) const
571 unsigned int len1 = valueFormats[0].get_len ();
572 unsigned int len2 = valueFormats[1].get_len ();
573 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
575 unsigned int count = len;
576 const PairValueRecord *record = CastP<PairValueRecord> (array);
577 for (unsigned int i = 0; i < count; i++)
579 if (c->buffer->info[pos].codepoint == record->secondGlyph)
581 valueFormats[0].apply_value (c->font, this, &record->values[0], c->buffer->pos[c->buffer->i]);
582 valueFormats[1].apply_value (c->font, this, &record->values[len1], c->buffer->pos[pos]);
588 record = &StructAtOffset<PairValueRecord> (record, record_size);
594 struct sanitize_closure_t {
596 ValueFormat *valueFormats;
597 unsigned int len1; /* valueFormats[0].get_len() */
598 unsigned int stride; /* 1 + len1 + len2 */
601 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
603 if (!(c->check_struct (this)
604 && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
606 unsigned int count = len;
607 PairValueRecord *record = CastP<PairValueRecord> (array);
608 return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
609 && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
613 USHORT len; /* Number of PairValueRecords */
614 USHORT array[VAR]; /* Array of PairValueRecords--ordered
615 * by GlyphID of the second glyph */
617 DEFINE_SIZE_ARRAY (2, array);
620 struct PairPosFormat1
622 friend struct PairPos;
625 inline bool apply (hb_apply_context_t *c) const
628 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
629 if (unlikely (c->buffer->i + 2 > end))
632 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
633 if (likely (index == NOT_COVERED))
636 unsigned int j = c->buffer->i + 1;
637 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
639 if (unlikely (j == end))
644 return (this+pairSet[index]).apply (c, &valueFormat1, j);
647 inline bool sanitize (hb_sanitize_context_t *c) {
650 unsigned int len1 = valueFormat1.get_len ();
651 unsigned int len2 = valueFormat2.get_len ();
652 PairSet::sanitize_closure_t closure = {
659 return c->check_struct (this)
660 && coverage.sanitize (c, this)
661 && pairSet.sanitize (c, this, &closure);
665 USHORT format; /* Format identifier--format = 1 */
667 coverage; /* Offset to Coverage table--from
668 * beginning of subtable */
669 ValueFormat valueFormat1; /* Defines the types of data in
670 * ValueRecord1--for the first glyph
671 * in the pair--may be zero (0) */
672 ValueFormat valueFormat2; /* Defines the types of data in
673 * ValueRecord2--for the second glyph
674 * in the pair--may be zero (0) */
675 OffsetArrayOf<PairSet>
676 pairSet; /* Array of PairSet tables
677 * ordered by Coverage Index */
679 DEFINE_SIZE_ARRAY (10, pairSet);
682 struct PairPosFormat2
684 friend struct PairPos;
687 inline bool apply (hb_apply_context_t *c) const
690 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
691 if (unlikely (c->buffer->i + 2 > end))
694 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
695 if (likely (index == NOT_COVERED))
698 unsigned int j = c->buffer->i + 1;
699 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
701 if (unlikely (j == end))
706 unsigned int len1 = valueFormat1.get_len ();
707 unsigned int len2 = valueFormat2.get_len ();
708 unsigned int record_len = len1 + len2;
710 unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->i].codepoint);
711 unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
712 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
715 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
716 valueFormat1.apply_value (c->font, this, v, c->buffer->pos[c->buffer->i]);
717 valueFormat2.apply_value (c->font, this, v + len1, c->buffer->pos[j]);
726 inline bool sanitize (hb_sanitize_context_t *c) {
728 if (!(c->check_struct (this)
729 && coverage.sanitize (c, this)
730 && classDef1.sanitize (c, this)
731 && classDef2.sanitize (c, this))) return false;
733 unsigned int len1 = valueFormat1.get_len ();
734 unsigned int len2 = valueFormat2.get_len ();
735 unsigned int stride = len1 + len2;
736 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
737 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
738 return c->check_array (values, record_size, count) &&
739 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
740 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
744 USHORT format; /* Format identifier--format = 2 */
746 coverage; /* Offset to Coverage table--from
747 * beginning of subtable */
748 ValueFormat valueFormat1; /* ValueRecord definition--for the
749 * first glyph of the pair--may be zero
751 ValueFormat valueFormat2; /* ValueRecord definition--for the
752 * second glyph of the pair--may be
755 classDef1; /* Offset to ClassDef table--from
756 * beginning of PairPos subtable--for
757 * the first glyph of the pair */
759 classDef2; /* Offset to ClassDef table--from
760 * beginning of PairPos subtable--for
761 * the second glyph of the pair */
762 USHORT class1Count; /* Number of classes in ClassDef1
763 * table--includes Class0 */
764 USHORT class2Count; /* Number of classes in ClassDef2
765 * table--includes Class0 */
766 ValueRecord values; /* Matrix of value pairs:
767 * class1-major, class2-minor,
768 * Each entry has value1 and value2 */
770 DEFINE_SIZE_ARRAY (16, values);
775 friend struct PosLookupSubTable;
778 inline bool apply (hb_apply_context_t *c) const
782 case 1: return u.format1.apply (c);
783 case 2: return u.format2.apply (c);
784 default:return false;
788 inline bool sanitize (hb_sanitize_context_t *c) {
790 if (!u.format.sanitize (c)) return false;
792 case 1: return u.format1.sanitize (c);
793 case 2: return u.format2.sanitize (c);
800 USHORT format; /* Format identifier */
801 PairPosFormat1 format1;
802 PairPosFormat2 format2;
807 struct EntryExitRecord
809 friend struct CursivePosFormat1;
811 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
813 return entryAnchor.sanitize (c, base)
814 && exitAnchor.sanitize (c, base);
819 entryAnchor; /* Offset to EntryAnchor table--from
820 * beginning of CursivePos
821 * subtable--may be NULL */
823 exitAnchor; /* Offset to ExitAnchor table--from
824 * beginning of CursivePos
825 * subtable--may be NULL */
827 DEFINE_SIZE_STATIC (4);
830 struct CursivePosFormat1
832 friend struct CursivePos;
835 inline bool apply (hb_apply_context_t *c) const
839 /* We don't handle mark glyphs here. */
840 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
843 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
844 if (unlikely (c->buffer->i + 2 > end))
847 const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->i].codepoint)];
848 if (!this_record.exitAnchor)
851 unsigned int j = c->buffer->i + 1;
852 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
854 if (unlikely (j == end))
859 const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)];
860 if (!next_record.entryAnchor)
863 unsigned int i = c->buffer->i;
865 hb_position_t entry_x, entry_y, exit_x, exit_y;
866 (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, c->direction, &exit_x, &exit_y);
867 (this+next_record.entryAnchor).get_anchor (c->font, c->buffer->info[j].codepoint, c->direction, &entry_x, &entry_y);
869 /* Align the exit anchor of the left/top glyph with the entry anchor of the right/bottom glyph
870 * by adjusting advance of the left/top glyph. */
871 if (HB_DIRECTION_IS_BACKWARD (c->direction))
873 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
874 c->buffer->pos[j].x_advance = c->buffer->pos[j].x_offset + entry_x - exit_x;
876 c->buffer->pos[j].y_advance = c->buffer->pos[j].y_offset + entry_y - exit_y;
880 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
881 c->buffer->pos[i].x_advance = c->buffer->pos[i].x_offset + exit_x - entry_x;
883 c->buffer->pos[i].y_advance = c->buffer->pos[i].y_offset + exit_y - entry_y;
886 if (c->lookup_props & LookupFlag::RightToLeft)
888 c->buffer->pos[i].cursive_chain() = j - i;
889 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
890 c->buffer->pos[i].y_offset = entry_y - exit_y;
892 c->buffer->pos[i].x_offset = entry_x - exit_x;
896 c->buffer->pos[j].cursive_chain() = i - j;
897 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
898 c->buffer->pos[j].y_offset = exit_y - entry_y;
900 c->buffer->pos[j].x_offset = exit_x - entry_x;
907 inline bool sanitize (hb_sanitize_context_t *c) {
909 return coverage.sanitize (c, this)
910 && entryExitRecord.sanitize (c, this);
914 USHORT format; /* Format identifier--format = 1 */
916 coverage; /* Offset to Coverage table--from
917 * beginning of subtable */
918 ArrayOf<EntryExitRecord>
919 entryExitRecord; /* Array of EntryExit records--in
920 * Coverage Index order */
922 DEFINE_SIZE_ARRAY (6, entryExitRecord);
927 friend struct PosLookupSubTable;
930 inline bool apply (hb_apply_context_t *c) const
934 case 1: return u.format1.apply (c);
935 default:return false;
939 inline bool sanitize (hb_sanitize_context_t *c) {
941 if (!u.format.sanitize (c)) return false;
943 case 1: return u.format1.sanitize (c);
950 USHORT format; /* Format identifier */
951 CursivePosFormat1 format1;
956 typedef AnchorMatrix BaseArray; /* base-major--
957 * in order of BaseCoverage Index--,
959 * ordered by class--zero-based. */
961 struct MarkBasePosFormat1
963 friend struct MarkBasePos;
966 inline bool apply (hb_apply_context_t *c) const
969 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
970 if (likely (mark_index == NOT_COVERED))
973 /* now we search backwards for a non-mark glyph */
974 unsigned int property;
975 unsigned int j = c->buffer->i;
981 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
983 /* The following assertion is too strong, so we've disabled it. */
984 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
987 unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
988 if (base_index == NOT_COVERED)
991 return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
994 inline bool sanitize (hb_sanitize_context_t *c) {
996 return c->check_struct (this)
997 && markCoverage.sanitize (c, this)
998 && baseCoverage.sanitize (c, this)
999 && markArray.sanitize (c, this)
1000 && baseArray.sanitize (c, this, (unsigned int) classCount);
1004 USHORT format; /* Format identifier--format = 1 */
1006 markCoverage; /* Offset to MarkCoverage table--from
1007 * beginning of MarkBasePos subtable */
1009 baseCoverage; /* Offset to BaseCoverage table--from
1010 * beginning of MarkBasePos subtable */
1011 USHORT classCount; /* Number of classes defined for marks */
1013 markArray; /* Offset to MarkArray table--from
1014 * beginning of MarkBasePos subtable */
1016 baseArray; /* Offset to BaseArray table--from
1017 * beginning of MarkBasePos subtable */
1019 DEFINE_SIZE_STATIC (12);
1024 friend struct PosLookupSubTable;
1027 inline bool apply (hb_apply_context_t *c) const
1031 case 1: return u.format1.apply (c);
1032 default:return false;
1036 inline bool sanitize (hb_sanitize_context_t *c) {
1038 if (!u.format.sanitize (c)) return false;
1040 case 1: return u.format1.sanitize (c);
1041 default:return true;
1047 USHORT format; /* Format identifier */
1048 MarkBasePosFormat1 format1;
1053 typedef AnchorMatrix LigatureAttach; /* component-major--
1054 * in order of writing direction--,
1056 * ordered by class--zero-based. */
1058 typedef OffsetListOf<LigatureAttach> LigatureArray;
1059 /* Array of LigatureAttach
1061 * LigatureCoverage Index */
1063 struct MarkLigPosFormat1
1065 friend struct MarkLigPos;
1068 inline bool apply (hb_apply_context_t *c) const
1071 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1072 if (likely (mark_index == NOT_COVERED))
1075 /* now we search backwards for a non-mark glyph */
1076 unsigned int property;
1077 unsigned int j = c->buffer->i;
1083 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1085 /* The following assertion is too strong, so we've disabled it. */
1086 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1089 unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1090 if (lig_index == NOT_COVERED)
1093 const LigatureArray& lig_array = this+ligatureArray;
1094 const LigatureAttach& lig_attach = lig_array[lig_index];
1096 /* Find component to attach to */
1097 unsigned int comp_count = lig_attach.rows;
1098 if (unlikely (!comp_count))
1100 unsigned int comp_index;
1101 /* We must now check whether the ligature ID of the current mark glyph
1102 * is identical to the ligature ID of the found ligature. If yes, we
1103 * can directly use the component index. If not, we attach the mark
1104 * glyph to the last component of the ligature. */
1105 if (c->buffer->info[j].lig_id() && c->buffer->info[j].lig_id() == c->buffer->info[c->buffer->i].lig_id() && c->buffer->info[c->buffer->i].lig_comp())
1107 comp_index = c->buffer->info[c->buffer->i].lig_comp() - 1;
1108 if (comp_index >= comp_count)
1109 comp_index = comp_count - 1;
1112 comp_index = comp_count - 1;
1114 return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1117 inline bool sanitize (hb_sanitize_context_t *c) {
1119 return c->check_struct (this)
1120 && markCoverage.sanitize (c, this)
1121 && ligatureCoverage.sanitize (c, this)
1122 && markArray.sanitize (c, this)
1123 && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1127 USHORT format; /* Format identifier--format = 1 */
1129 markCoverage; /* Offset to Mark Coverage table--from
1130 * beginning of MarkLigPos subtable */
1132 ligatureCoverage; /* Offset to Ligature Coverage
1133 * table--from beginning of MarkLigPos
1135 USHORT classCount; /* Number of defined mark classes */
1137 markArray; /* Offset to MarkArray table--from
1138 * beginning of MarkLigPos subtable */
1139 OffsetTo<LigatureArray>
1140 ligatureArray; /* Offset to LigatureArray table--from
1141 * beginning of MarkLigPos subtable */
1143 DEFINE_SIZE_STATIC (12);
1148 friend struct PosLookupSubTable;
1151 inline bool apply (hb_apply_context_t *c) const
1155 case 1: return u.format1.apply (c);
1156 default:return false;
1160 inline bool sanitize (hb_sanitize_context_t *c) {
1162 if (!u.format.sanitize (c)) return false;
1164 case 1: return u.format1.sanitize (c);
1165 default:return true;
1171 USHORT format; /* Format identifier */
1172 MarkLigPosFormat1 format1;
1177 typedef AnchorMatrix Mark2Array; /* mark2-major--
1178 * in order of Mark2Coverage Index--,
1180 * ordered by class--zero-based. */
1182 struct MarkMarkPosFormat1
1184 friend struct MarkMarkPos;
1187 inline bool apply (hb_apply_context_t *c) const
1190 unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
1191 if (likely (mark1_index == NOT_COVERED))
1194 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1195 unsigned int property;
1196 unsigned int j = c->buffer->i;
1202 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property));
1204 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1207 /* Two marks match only if they belong to the same base, or same component
1208 * of the same ligature. That is, the component numbers must match, and
1209 * if those are non-zero, the ligid number should also match. */
1210 if ((c->buffer->info[j].lig_comp() != c->buffer->info[c->buffer->i].lig_comp()) ||
1211 (c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->i].lig_id()))
1214 unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1215 if (mark2_index == NOT_COVERED)
1218 return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1221 inline bool sanitize (hb_sanitize_context_t *c) {
1223 return c->check_struct (this)
1224 && mark1Coverage.sanitize (c, this)
1225 && mark2Coverage.sanitize (c, this)
1226 && mark1Array.sanitize (c, this)
1227 && mark2Array.sanitize (c, this, (unsigned int) classCount);
1231 USHORT format; /* Format identifier--format = 1 */
1233 mark1Coverage; /* Offset to Combining Mark1 Coverage
1234 * table--from beginning of MarkMarkPos
1237 mark2Coverage; /* Offset to Combining Mark2 Coverage
1238 * table--from beginning of MarkMarkPos
1240 USHORT classCount; /* Number of defined mark classes */
1242 mark1Array; /* Offset to Mark1Array table--from
1243 * beginning of MarkMarkPos subtable */
1244 OffsetTo<Mark2Array>
1245 mark2Array; /* Offset to Mark2Array table--from
1246 * beginning of MarkMarkPos subtable */
1248 DEFINE_SIZE_STATIC (12);
1253 friend struct PosLookupSubTable;
1256 inline bool apply (hb_apply_context_t *c) const
1260 case 1: return u.format1.apply (c);
1261 default:return false;
1265 inline bool sanitize (hb_sanitize_context_t *c) {
1267 if (!u.format.sanitize (c)) return false;
1269 case 1: return u.format1.sanitize (c);
1270 default:return true;
1276 USHORT format; /* Format identifier */
1277 MarkMarkPosFormat1 format1;
1283 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1286 struct ContextPos : Context
1288 friend struct PosLookupSubTable;
1291 inline bool apply (hb_apply_context_t *c) const
1294 return Context::apply (c, position_lookup);
1298 struct ChainContextPos : ChainContext
1300 friend struct PosLookupSubTable;
1303 inline bool apply (hb_apply_context_t *c) const
1306 return ChainContext::apply (c, position_lookup);
1311 struct ExtensionPos : Extension
1313 friend struct PosLookupSubTable;
1316 inline const struct PosLookupSubTable& get_subtable (void) const
1318 unsigned int offset = get_offset ();
1319 if (unlikely (!offset)) return Null(PosLookupSubTable);
1320 return StructAtOffset<PosLookupSubTable> (this, offset);
1323 inline bool apply (hb_apply_context_t *c) const;
1325 inline bool sanitize (hb_sanitize_context_t *c);
1335 struct PosLookupSubTable
1337 friend struct PosLookup;
1351 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1354 switch (lookup_type) {
1355 case Single: return u.single.apply (c);
1356 case Pair: return u.pair.apply (c);
1357 case Cursive: return u.cursive.apply (c);
1358 case MarkBase: return u.markBase.apply (c);
1359 case MarkLig: return u.markLig.apply (c);
1360 case MarkMark: return u.markMark.apply (c);
1361 case Context: return u.c.apply (c);
1362 case ChainContext: return u.chainContext.apply (c);
1363 case Extension: return u.extension.apply (c);
1364 default:return false;
1368 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1370 switch (lookup_type) {
1371 case Single: return u.single.sanitize (c);
1372 case Pair: return u.pair.sanitize (c);
1373 case Cursive: return u.cursive.sanitize (c);
1374 case MarkBase: return u.markBase.sanitize (c);
1375 case MarkLig: return u.markLig.sanitize (c);
1376 case MarkMark: return u.markMark.sanitize (c);
1377 case Context: return u.c.sanitize (c);
1378 case ChainContext: return u.chainContext.sanitize (c);
1379 case Extension: return u.extension.sanitize (c);
1380 default:return true;
1390 MarkBasePos markBase;
1392 MarkMarkPos markMark;
1394 ChainContextPos chainContext;
1395 ExtensionPos extension;
1398 DEFINE_SIZE_UNION (2, sub_format);
1402 struct PosLookup : Lookup
1404 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1405 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1407 inline bool apply_once (hb_font_t *font,
1408 hb_buffer_t *buffer,
1409 hb_mask_t lookup_mask,
1410 unsigned int context_length,
1411 unsigned int nesting_level_left) const
1413 unsigned int lookup_type = get_type ();
1414 hb_apply_context_t c[1] = {{0}};
1417 c->face = font->face;
1419 c->direction = buffer->props.direction;
1420 c->lookup_mask = lookup_mask;
1421 c->context_length = context_length;
1422 c->nesting_level_left = nesting_level_left;
1423 c->lookup_props = get_props ();
1425 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, &c->property))
1428 for (unsigned int i = 0; i < get_subtable_count (); i++)
1429 if (get_subtable (i).apply (c, lookup_type))
1435 inline bool apply_string (hb_font_t *font,
1436 hb_buffer_t *buffer,
1437 hb_mask_t mask) const
1441 if (unlikely (!buffer->len))
1445 while (buffer->i < buffer->len)
1447 if ((buffer->info[buffer->i].mask & mask) &&
1448 apply_once (font, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
1457 inline bool sanitize (hb_sanitize_context_t *c) {
1459 if (unlikely (!Lookup::sanitize (c))) return false;
1460 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1461 return list.sanitize (c, this, get_type ());
1465 typedef OffsetListOf<PosLookup> PosLookupList;
1471 struct GPOS : GSUBGPOS
1473 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1475 inline const PosLookup& get_lookup (unsigned int i) const
1476 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1478 inline bool position_lookup (hb_font_t *font,
1479 hb_buffer_t *buffer,
1480 unsigned int lookup_index,
1481 hb_mask_t mask) const
1482 { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
1484 static inline void position_finish (hb_buffer_t *buffer);
1486 inline bool sanitize (hb_sanitize_context_t *c) {
1488 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1489 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1490 return list.sanitize (c, this);
1493 DEFINE_SIZE_STATIC (10);
1497 GPOS::position_finish (hb_buffer_t *buffer)
1501 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
1502 hb_direction_t direction = buffer->props.direction;
1504 /* Handle cursive connections:
1505 * First handle all chain-back connections, then handle all chain-forward connections. */
1506 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
1508 for (j = 0; j < len; j++) {
1509 if (pos[j].cursive_chain() < 0)
1510 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
1512 for (i = len; i > 0; i--) {
1514 if (pos[j].cursive_chain() > 0)
1515 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
1520 for (j = 0; j < len; j++) {
1521 if (pos[j].cursive_chain() < 0)
1522 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
1524 for (i = len; i > 0; i--) {
1526 if (pos[j].cursive_chain() > 0)
1527 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
1532 /* Handle attachments */
1533 for (i = 0; i < len; i++)
1534 if (pos[i].attach_lookback())
1536 unsigned int back = i - pos[i].attach_lookback();
1537 pos[i].x_offset += pos[back].x_offset;
1538 pos[i].y_offset += pos[back].y_offset;
1540 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
1541 for (j = back + 1; j < i + 1; j++) {
1542 pos[i].x_offset += pos[j].x_advance;
1543 pos[i].y_offset += pos[j].y_advance;
1546 for (j = back; j < i; j++) {
1547 pos[i].x_offset -= pos[j].x_advance;
1548 pos[i].y_offset -= pos[j].y_advance;
1554 /* Out-of-class implementation for methods recursing */
1556 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1559 return get_subtable ().apply (c, get_type ());
1562 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1565 if (unlikely (!Extension::sanitize (c))) return false;
1566 unsigned int offset = get_offset ();
1567 if (unlikely (!offset)) return true;
1568 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1571 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1573 const GPOS &gpos = *(c->face->ot_layout->gpos);
1574 const PosLookup &l = gpos.get_lookup (lookup_index);
1576 if (unlikely (c->nesting_level_left == 0))
1579 if (unlikely (c->context_length < 1))
1582 return l.apply_once (c->font, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
1586 #undef attach_lookback
1587 #undef cursive_chain
1592 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */