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->scale_x (get_short (values++));
109 if (format & yPlacement) glyph_pos.y_offset += font->scale_y (get_short (values++));
110 if (format & xAdvance) glyph_pos.x_advance += font->scale_x (get_short (values++));
111 if (format & yAdvance) glyph_pos.y_advance += font->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_position_t *x, hb_position_t *y) const
215 *x = font->scale_x (xCoordinate);
216 *y = font->scale_y (yCoordinate);
219 inline bool sanitize (hb_sanitize_context_t *c) {
221 return c->check_struct (this);
225 USHORT format; /* Format identifier--format = 1 */
226 SHORT xCoordinate; /* Horizontal value--in design units */
227 SHORT yCoordinate; /* Vertical value--in design units */
229 DEFINE_SIZE_STATIC (6);
234 friend struct Anchor;
237 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
238 hb_position_t *x, hb_position_t *y) const
240 unsigned int x_ppem = font->x_ppem;
241 unsigned int y_ppem = font->y_ppem;
242 hb_position_t cx, cy;
243 hb_bool_t ret = false;
245 if (x_ppem || y_ppem)
246 ret = hb_font_get_contour_point (font, anchorPoint, glyph_id, &cx, &cy);
247 *x = x_ppem && ret ? cx : font->scale_x (xCoordinate);
248 *y = y_ppem && ret ? cy : font->scale_y (yCoordinate);
251 inline bool sanitize (hb_sanitize_context_t *c) {
253 return c->check_struct (this);
257 USHORT format; /* Format identifier--format = 2 */
258 SHORT xCoordinate; /* Horizontal value--in design units */
259 SHORT yCoordinate; /* Vertical value--in design units */
260 USHORT anchorPoint; /* Index to glyph contour point */
262 DEFINE_SIZE_STATIC (8);
267 friend struct Anchor;
270 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
271 hb_position_t *x, hb_position_t *y) const
273 *x = font->scale_x (xCoordinate);
274 *y = font->scale_y (yCoordinate);
276 /* pixel -> fractional pixel */
278 *x += (this+xDeviceTable).get_x_delta (font);
280 *y += (this+yDeviceTable).get_x_delta (font);
283 inline bool sanitize (hb_sanitize_context_t *c) {
285 return c->check_struct (this)
286 && xDeviceTable.sanitize (c, this)
287 && yDeviceTable.sanitize (c, this);
291 USHORT format; /* Format identifier--format = 3 */
292 SHORT xCoordinate; /* Horizontal value--in design units */
293 SHORT yCoordinate; /* Vertical value--in design units */
295 xDeviceTable; /* Offset to Device table for X
296 * coordinate-- from beginning of
297 * Anchor table (may be NULL) */
299 yDeviceTable; /* Offset to Device table for Y
300 * coordinate-- from beginning of
301 * Anchor table (may be NULL) */
303 DEFINE_SIZE_STATIC (10);
308 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
309 hb_position_t *x, hb_position_t *y) const
313 case 1: u.format1.get_anchor (font, glyph_id, x, y); return;
314 case 2: u.format2.get_anchor (font, glyph_id, x, y); return;
315 case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
320 inline bool sanitize (hb_sanitize_context_t *c) {
322 if (!u.format.sanitize (c)) return false;
324 case 1: return u.format1.sanitize (c);
325 case 2: return u.format2.sanitize (c);
326 case 3: return u.format3.sanitize (c);
333 USHORT format; /* Format identifier */
334 AnchorFormat1 format1;
335 AnchorFormat2 format2;
336 AnchorFormat3 format3;
339 DEFINE_SIZE_UNION (2, format);
345 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
346 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
347 return this+matrix[row * cols + col];
350 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
352 if (!c->check_struct (this)) return false;
353 if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false;
354 unsigned int count = rows * cols;
355 if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
356 for (unsigned int i = 0; i < count; i++)
357 if (!matrix[i].sanitize (c, this)) return false;
361 USHORT rows; /* Number of rows */
364 matrix[VAR]; /* Matrix of offsets to Anchor tables--
365 * from beginning of AnchorMatrix table */
367 DEFINE_SIZE_ARRAY (2, matrix);
373 friend struct MarkArray;
375 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
377 return c->check_struct (this)
378 && markAnchor.sanitize (c, base);
382 USHORT klass; /* Class defined for this mark */
384 markAnchor; /* Offset to Anchor table--from
385 * beginning of MarkArray table */
387 DEFINE_SIZE_STATIC (4);
390 struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */
392 inline bool apply (hb_apply_context_t *c,
393 unsigned int mark_index, unsigned int glyph_index,
394 const AnchorMatrix &anchors, unsigned int class_count,
395 unsigned int glyph_pos) const
398 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
399 unsigned int mark_class = record.klass;
401 const Anchor& mark_anchor = this + record.markAnchor;
402 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
404 hb_position_t mark_x, mark_y, base_x, base_y;
406 mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->i].codepoint, &mark_x, &mark_y);
407 glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
409 hb_glyph_position_t &o = c->buffer->pos[c->buffer->i];
410 o.x_offset = base_x - mark_x;
411 o.y_offset = base_y - mark_y;
412 o.attach_lookback() = c->buffer->i - glyph_pos;
418 inline bool sanitize (hb_sanitize_context_t *c) {
420 return ArrayOf<MarkRecord>::sanitize (c, this);
427 struct SinglePosFormat1
429 friend struct SinglePos;
432 inline bool apply (hb_apply_context_t *c) const
435 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
436 if (likely (index == NOT_COVERED))
439 valueFormat.apply_value (c->font, this, values, c->buffer->pos[c->buffer->i]);
445 inline bool sanitize (hb_sanitize_context_t *c) {
447 return c->check_struct (this)
448 && coverage.sanitize (c, this)
449 && valueFormat.sanitize_value (c, this, values);
453 USHORT format; /* Format identifier--format = 1 */
455 coverage; /* Offset to Coverage table--from
456 * beginning of subtable */
457 ValueFormat valueFormat; /* Defines the types of data in the
459 ValueRecord values; /* Defines positioning
460 * value(s)--applied to all glyphs in
461 * the Coverage table */
463 DEFINE_SIZE_ARRAY (6, values);
466 struct SinglePosFormat2
468 friend struct SinglePos;
471 inline bool apply (hb_apply_context_t *c) const
474 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
475 if (likely (index == NOT_COVERED))
478 if (likely (index >= valueCount))
481 valueFormat.apply_value (c->font, this,
482 &values[index * valueFormat.get_len ()],
483 c->buffer->pos[c->buffer->i]);
489 inline bool sanitize (hb_sanitize_context_t *c) {
491 return c->check_struct (this)
492 && coverage.sanitize (c, this)
493 && valueFormat.sanitize_values (c, this, values, valueCount);
497 USHORT format; /* Format identifier--format = 2 */
499 coverage; /* Offset to Coverage table--from
500 * beginning of subtable */
501 ValueFormat valueFormat; /* Defines the types of data in the
503 USHORT valueCount; /* Number of ValueRecords */
504 ValueRecord values; /* Array of ValueRecords--positioning
505 * values applied to glyphs */
507 DEFINE_SIZE_ARRAY (8, values);
512 friend struct PosLookupSubTable;
515 inline bool apply (hb_apply_context_t *c) const
519 case 1: return u.format1.apply (c);
520 case 2: return u.format2.apply (c);
521 default:return false;
525 inline bool sanitize (hb_sanitize_context_t *c) {
527 if (!u.format.sanitize (c)) return false;
529 case 1: return u.format1.sanitize (c);
530 case 2: return u.format2.sanitize (c);
537 USHORT format; /* Format identifier */
538 SinglePosFormat1 format1;
539 SinglePosFormat2 format2;
544 struct PairValueRecord
546 friend struct PairSet;
549 GlyphID secondGlyph; /* GlyphID of second glyph in the
550 * pair--first glyph is listed in the
552 ValueRecord values; /* Positioning data for the first glyph
553 * followed by for second glyph */
555 DEFINE_SIZE_ARRAY (2, values);
560 friend struct PairPosFormat1;
562 inline bool apply (hb_apply_context_t *c,
563 const ValueFormat *valueFormats,
564 unsigned int pos) const
567 unsigned int len1 = valueFormats[0].get_len ();
568 unsigned int len2 = valueFormats[1].get_len ();
569 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
571 unsigned int count = len;
572 const PairValueRecord *record = CastP<PairValueRecord> (array);
573 for (unsigned int i = 0; i < count; i++)
575 if (c->buffer->info[pos].codepoint == record->secondGlyph)
577 valueFormats[0].apply_value (c->font, this, &record->values[0], c->buffer->pos[c->buffer->i]);
578 valueFormats[1].apply_value (c->font, this, &record->values[len1], c->buffer->pos[pos]);
584 record = &StructAtOffset<PairValueRecord> (record, record_size);
590 struct sanitize_closure_t {
592 ValueFormat *valueFormats;
593 unsigned int len1; /* valueFormats[0].get_len() */
594 unsigned int stride; /* 1 + len1 + len2 */
597 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
599 if (!(c->check_struct (this)
600 && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
602 unsigned int count = len;
603 PairValueRecord *record = CastP<PairValueRecord> (array);
604 return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
605 && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
609 USHORT len; /* Number of PairValueRecords */
610 USHORT array[VAR]; /* Array of PairValueRecords--ordered
611 * by GlyphID of the second glyph */
613 DEFINE_SIZE_ARRAY (2, array);
616 struct PairPosFormat1
618 friend struct PairPos;
621 inline bool apply (hb_apply_context_t *c) const
624 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
625 if (unlikely (c->buffer->i + 2 > end))
628 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
629 if (likely (index == NOT_COVERED))
632 unsigned int j = c->buffer->i + 1;
633 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
635 if (unlikely (j == end))
640 return (this+pairSet[index]).apply (c, &valueFormat1, j);
643 inline bool sanitize (hb_sanitize_context_t *c) {
646 unsigned int len1 = valueFormat1.get_len ();
647 unsigned int len2 = valueFormat2.get_len ();
648 PairSet::sanitize_closure_t closure = {
655 return c->check_struct (this)
656 && coverage.sanitize (c, this)
657 && pairSet.sanitize (c, this, &closure);
661 USHORT format; /* Format identifier--format = 1 */
663 coverage; /* Offset to Coverage table--from
664 * beginning of subtable */
665 ValueFormat valueFormat1; /* Defines the types of data in
666 * ValueRecord1--for the first glyph
667 * in the pair--may be zero (0) */
668 ValueFormat valueFormat2; /* Defines the types of data in
669 * ValueRecord2--for the second glyph
670 * in the pair--may be zero (0) */
671 OffsetArrayOf<PairSet>
672 pairSet; /* Array of PairSet tables
673 * ordered by Coverage Index */
675 DEFINE_SIZE_ARRAY (10, pairSet);
678 struct PairPosFormat2
680 friend struct PairPos;
683 inline bool apply (hb_apply_context_t *c) const
686 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
687 if (unlikely (c->buffer->i + 2 > end))
690 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
691 if (likely (index == NOT_COVERED))
694 unsigned int j = c->buffer->i + 1;
695 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
697 if (unlikely (j == end))
702 unsigned int len1 = valueFormat1.get_len ();
703 unsigned int len2 = valueFormat2.get_len ();
704 unsigned int record_len = len1 + len2;
706 unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->i].codepoint);
707 unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
708 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
711 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
712 valueFormat1.apply_value (c->font, this, v, c->buffer->pos[c->buffer->i]);
713 valueFormat2.apply_value (c->font, this, v + len1, c->buffer->pos[j]);
722 inline bool sanitize (hb_sanitize_context_t *c) {
724 if (!(c->check_struct (this)
725 && coverage.sanitize (c, this)
726 && classDef1.sanitize (c, this)
727 && classDef2.sanitize (c, this))) return false;
729 unsigned int len1 = valueFormat1.get_len ();
730 unsigned int len2 = valueFormat2.get_len ();
731 unsigned int stride = len1 + len2;
732 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
733 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
734 return c->check_array (values, record_size, count) &&
735 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
736 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
740 USHORT format; /* Format identifier--format = 2 */
742 coverage; /* Offset to Coverage table--from
743 * beginning of subtable */
744 ValueFormat valueFormat1; /* ValueRecord definition--for the
745 * first glyph of the pair--may be zero
747 ValueFormat valueFormat2; /* ValueRecord definition--for the
748 * second glyph of the pair--may be
751 classDef1; /* Offset to ClassDef table--from
752 * beginning of PairPos subtable--for
753 * the first glyph of the pair */
755 classDef2; /* Offset to ClassDef table--from
756 * beginning of PairPos subtable--for
757 * the second glyph of the pair */
758 USHORT class1Count; /* Number of classes in ClassDef1
759 * table--includes Class0 */
760 USHORT class2Count; /* Number of classes in ClassDef2
761 * table--includes Class0 */
762 ValueRecord values; /* Matrix of value pairs:
763 * class1-major, class2-minor,
764 * Each entry has value1 and value2 */
766 DEFINE_SIZE_ARRAY (16, values);
771 friend struct PosLookupSubTable;
774 inline bool apply (hb_apply_context_t *c) const
778 case 1: return u.format1.apply (c);
779 case 2: return u.format2.apply (c);
780 default:return false;
784 inline bool sanitize (hb_sanitize_context_t *c) {
786 if (!u.format.sanitize (c)) return false;
788 case 1: return u.format1.sanitize (c);
789 case 2: return u.format2.sanitize (c);
796 USHORT format; /* Format identifier */
797 PairPosFormat1 format1;
798 PairPosFormat2 format2;
803 struct EntryExitRecord
805 friend struct CursivePosFormat1;
807 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
809 return entryAnchor.sanitize (c, base)
810 && exitAnchor.sanitize (c, base);
815 entryAnchor; /* Offset to EntryAnchor table--from
816 * beginning of CursivePos
817 * subtable--may be NULL */
819 exitAnchor; /* Offset to ExitAnchor table--from
820 * beginning of CursivePos
821 * subtable--may be NULL */
823 DEFINE_SIZE_STATIC (4);
826 struct CursivePosFormat1
828 friend struct CursivePos;
831 inline bool apply (hb_apply_context_t *c) const
835 /* We don't handle mark glyphs here. */
836 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
839 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
840 if (unlikely (c->buffer->i + 2 > end))
843 const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->i].codepoint)];
844 if (!this_record.exitAnchor)
847 unsigned int j = c->buffer->i + 1;
848 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
850 if (unlikely (j == end))
855 const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)];
856 if (!next_record.entryAnchor)
859 unsigned int i = c->buffer->i;
861 hb_position_t entry_x, entry_y, exit_x, exit_y;
862 (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, &exit_x, &exit_y);
863 (this+next_record.entryAnchor).get_anchor (c->font, c->buffer->info[j].codepoint, &entry_x, &entry_y);
865 hb_direction_t direction = c->buffer->props.direction;
867 /* Align the exit anchor of the left/top glyph with the entry anchor of the right/bottom glyph
868 * by adjusting advance of the left/top glyph. */
869 if (HB_DIRECTION_IS_BACKWARD (direction))
871 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
872 c->buffer->pos[j].x_advance = c->buffer->pos[j].x_offset + entry_x - exit_x;
874 c->buffer->pos[j].y_advance = c->buffer->pos[j].y_offset + entry_y - exit_y;
878 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
879 c->buffer->pos[i].x_advance = c->buffer->pos[i].x_offset + exit_x - entry_x;
881 c->buffer->pos[i].y_advance = c->buffer->pos[i].y_offset + exit_y - entry_y;
884 if (c->lookup_props & LookupFlag::RightToLeft)
886 c->buffer->pos[i].cursive_chain() = j - i;
887 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
888 c->buffer->pos[i].y_offset = entry_y - exit_y;
890 c->buffer->pos[i].x_offset = entry_x - exit_x;
894 c->buffer->pos[j].cursive_chain() = i - j;
895 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
896 c->buffer->pos[j].y_offset = exit_y - entry_y;
898 c->buffer->pos[j].x_offset = exit_x - entry_x;
905 inline bool sanitize (hb_sanitize_context_t *c) {
907 return coverage.sanitize (c, this)
908 && entryExitRecord.sanitize (c, this);
912 USHORT format; /* Format identifier--format = 1 */
914 coverage; /* Offset to Coverage table--from
915 * beginning of subtable */
916 ArrayOf<EntryExitRecord>
917 entryExitRecord; /* Array of EntryExit records--in
918 * Coverage Index order */
920 DEFINE_SIZE_ARRAY (6, entryExitRecord);
925 friend struct PosLookupSubTable;
928 inline bool apply (hb_apply_context_t *c) const
932 case 1: return u.format1.apply (c);
933 default:return false;
937 inline bool sanitize (hb_sanitize_context_t *c) {
939 if (!u.format.sanitize (c)) return false;
941 case 1: return u.format1.sanitize (c);
948 USHORT format; /* Format identifier */
949 CursivePosFormat1 format1;
954 typedef AnchorMatrix BaseArray; /* base-major--
955 * in order of BaseCoverage Index--,
957 * ordered by class--zero-based. */
959 struct MarkBasePosFormat1
961 friend struct MarkBasePos;
964 inline bool apply (hb_apply_context_t *c) const
967 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
968 if (likely (mark_index == NOT_COVERED))
971 /* now we search backwards for a non-mark glyph */
972 unsigned int property;
973 unsigned int j = c->buffer->i;
979 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
981 /* The following assertion is too strong, so we've disabled it. */
982 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
985 unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
986 if (base_index == NOT_COVERED)
989 return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
992 inline bool sanitize (hb_sanitize_context_t *c) {
994 return c->check_struct (this)
995 && markCoverage.sanitize (c, this)
996 && baseCoverage.sanitize (c, this)
997 && markArray.sanitize (c, this)
998 && baseArray.sanitize (c, this, (unsigned int) classCount);
1002 USHORT format; /* Format identifier--format = 1 */
1004 markCoverage; /* Offset to MarkCoverage table--from
1005 * beginning of MarkBasePos subtable */
1007 baseCoverage; /* Offset to BaseCoverage table--from
1008 * beginning of MarkBasePos subtable */
1009 USHORT classCount; /* Number of classes defined for marks */
1011 markArray; /* Offset to MarkArray table--from
1012 * beginning of MarkBasePos subtable */
1014 baseArray; /* Offset to BaseArray table--from
1015 * beginning of MarkBasePos subtable */
1017 DEFINE_SIZE_STATIC (12);
1022 friend struct PosLookupSubTable;
1025 inline bool apply (hb_apply_context_t *c) const
1029 case 1: return u.format1.apply (c);
1030 default:return false;
1034 inline bool sanitize (hb_sanitize_context_t *c) {
1036 if (!u.format.sanitize (c)) return false;
1038 case 1: return u.format1.sanitize (c);
1039 default:return true;
1045 USHORT format; /* Format identifier */
1046 MarkBasePosFormat1 format1;
1051 typedef AnchorMatrix LigatureAttach; /* component-major--
1052 * in order of writing direction--,
1054 * ordered by class--zero-based. */
1056 typedef OffsetListOf<LigatureAttach> LigatureArray;
1057 /* Array of LigatureAttach
1059 * LigatureCoverage Index */
1061 struct MarkLigPosFormat1
1063 friend struct MarkLigPos;
1066 inline bool apply (hb_apply_context_t *c) const
1069 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1070 if (likely (mark_index == NOT_COVERED))
1073 /* now we search backwards for a non-mark glyph */
1074 unsigned int property;
1075 unsigned int j = c->buffer->i;
1081 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1083 /* The following assertion is too strong, so we've disabled it. */
1084 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1087 unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1088 if (lig_index == NOT_COVERED)
1091 const LigatureArray& lig_array = this+ligatureArray;
1092 const LigatureAttach& lig_attach = lig_array[lig_index];
1094 /* Find component to attach to */
1095 unsigned int comp_count = lig_attach.rows;
1096 if (unlikely (!comp_count))
1098 unsigned int comp_index;
1099 /* We must now check whether the ligature ID of the current mark glyph
1100 * is identical to the ligature ID of the found ligature. If yes, we
1101 * can directly use the component index. If not, we attach the mark
1102 * glyph to the last component of the ligature. */
1103 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())
1105 comp_index = c->buffer->info[c->buffer->i].lig_comp() - 1;
1106 if (comp_index >= comp_count)
1107 comp_index = comp_count - 1;
1110 comp_index = comp_count - 1;
1112 return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1115 inline bool sanitize (hb_sanitize_context_t *c) {
1117 return c->check_struct (this)
1118 && markCoverage.sanitize (c, this)
1119 && ligatureCoverage.sanitize (c, this)
1120 && markArray.sanitize (c, this)
1121 && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1125 USHORT format; /* Format identifier--format = 1 */
1127 markCoverage; /* Offset to Mark Coverage table--from
1128 * beginning of MarkLigPos subtable */
1130 ligatureCoverage; /* Offset to Ligature Coverage
1131 * table--from beginning of MarkLigPos
1133 USHORT classCount; /* Number of defined mark classes */
1135 markArray; /* Offset to MarkArray table--from
1136 * beginning of MarkLigPos subtable */
1137 OffsetTo<LigatureArray>
1138 ligatureArray; /* Offset to LigatureArray table--from
1139 * beginning of MarkLigPos subtable */
1141 DEFINE_SIZE_STATIC (12);
1146 friend struct PosLookupSubTable;
1149 inline bool apply (hb_apply_context_t *c) const
1153 case 1: return u.format1.apply (c);
1154 default:return false;
1158 inline bool sanitize (hb_sanitize_context_t *c) {
1160 if (!u.format.sanitize (c)) return false;
1162 case 1: return u.format1.sanitize (c);
1163 default:return true;
1169 USHORT format; /* Format identifier */
1170 MarkLigPosFormat1 format1;
1175 typedef AnchorMatrix Mark2Array; /* mark2-major--
1176 * in order of Mark2Coverage Index--,
1178 * ordered by class--zero-based. */
1180 struct MarkMarkPosFormat1
1182 friend struct MarkMarkPos;
1185 inline bool apply (hb_apply_context_t *c) const
1188 unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
1189 if (likely (mark1_index == NOT_COVERED))
1192 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1193 unsigned int property;
1194 unsigned int j = c->buffer->i;
1200 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property));
1202 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1205 /* Two marks match only if they belong to the same base, or same component
1206 * of the same ligature. That is, the component numbers must match, and
1207 * if those are non-zero, the ligid number should also match. */
1208 if ((c->buffer->info[j].lig_comp() != c->buffer->info[c->buffer->i].lig_comp()) ||
1209 (c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->i].lig_id()))
1212 unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1213 if (mark2_index == NOT_COVERED)
1216 return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1219 inline bool sanitize (hb_sanitize_context_t *c) {
1221 return c->check_struct (this)
1222 && mark1Coverage.sanitize (c, this)
1223 && mark2Coverage.sanitize (c, this)
1224 && mark1Array.sanitize (c, this)
1225 && mark2Array.sanitize (c, this, (unsigned int) classCount);
1229 USHORT format; /* Format identifier--format = 1 */
1231 mark1Coverage; /* Offset to Combining Mark1 Coverage
1232 * table--from beginning of MarkMarkPos
1235 mark2Coverage; /* Offset to Combining Mark2 Coverage
1236 * table--from beginning of MarkMarkPos
1238 USHORT classCount; /* Number of defined mark classes */
1240 mark1Array; /* Offset to Mark1Array table--from
1241 * beginning of MarkMarkPos subtable */
1242 OffsetTo<Mark2Array>
1243 mark2Array; /* Offset to Mark2Array table--from
1244 * beginning of MarkMarkPos subtable */
1246 DEFINE_SIZE_STATIC (12);
1251 friend struct PosLookupSubTable;
1254 inline bool apply (hb_apply_context_t *c) const
1258 case 1: return u.format1.apply (c);
1259 default:return false;
1263 inline bool sanitize (hb_sanitize_context_t *c) {
1265 if (!u.format.sanitize (c)) return false;
1267 case 1: return u.format1.sanitize (c);
1268 default:return true;
1274 USHORT format; /* Format identifier */
1275 MarkMarkPosFormat1 format1;
1281 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1284 struct ContextPos : Context
1286 friend struct PosLookupSubTable;
1289 inline bool apply (hb_apply_context_t *c) const
1292 return Context::apply (c, position_lookup);
1296 struct ChainContextPos : ChainContext
1298 friend struct PosLookupSubTable;
1301 inline bool apply (hb_apply_context_t *c) const
1304 return ChainContext::apply (c, position_lookup);
1309 struct ExtensionPos : Extension
1311 friend struct PosLookupSubTable;
1314 inline const struct PosLookupSubTable& get_subtable (void) const
1316 unsigned int offset = get_offset ();
1317 if (unlikely (!offset)) return Null(PosLookupSubTable);
1318 return StructAtOffset<PosLookupSubTable> (this, offset);
1321 inline bool apply (hb_apply_context_t *c) const;
1323 inline bool sanitize (hb_sanitize_context_t *c);
1333 struct PosLookupSubTable
1335 friend struct PosLookup;
1349 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1352 switch (lookup_type) {
1353 case Single: return u.single.apply (c);
1354 case Pair: return u.pair.apply (c);
1355 case Cursive: return u.cursive.apply (c);
1356 case MarkBase: return u.markBase.apply (c);
1357 case MarkLig: return u.markLig.apply (c);
1358 case MarkMark: return u.markMark.apply (c);
1359 case Context: return u.c.apply (c);
1360 case ChainContext: return u.chainContext.apply (c);
1361 case Extension: return u.extension.apply (c);
1362 default:return false;
1366 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1368 switch (lookup_type) {
1369 case Single: return u.single.sanitize (c);
1370 case Pair: return u.pair.sanitize (c);
1371 case Cursive: return u.cursive.sanitize (c);
1372 case MarkBase: return u.markBase.sanitize (c);
1373 case MarkLig: return u.markLig.sanitize (c);
1374 case MarkMark: return u.markMark.sanitize (c);
1375 case Context: return u.c.sanitize (c);
1376 case ChainContext: return u.chainContext.sanitize (c);
1377 case Extension: return u.extension.sanitize (c);
1378 default:return true;
1388 MarkBasePos markBase;
1390 MarkMarkPos markMark;
1392 ChainContextPos chainContext;
1393 ExtensionPos extension;
1396 DEFINE_SIZE_UNION (2, sub_format);
1400 struct PosLookup : Lookup
1402 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1403 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1405 inline bool apply_once (hb_font_t *font,
1406 hb_buffer_t *buffer,
1407 hb_mask_t lookup_mask,
1408 unsigned int context_length,
1409 unsigned int nesting_level_left) const
1411 unsigned int lookup_type = get_type ();
1412 hb_apply_context_t c[1] = {{0}};
1415 c->face = font->face;
1417 c->lookup_mask = lookup_mask;
1418 c->context_length = context_length;
1419 c->nesting_level_left = nesting_level_left;
1420 c->lookup_props = get_props ();
1422 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, &c->property))
1425 for (unsigned int i = 0; i < get_subtable_count (); i++)
1426 if (get_subtable (i).apply (c, lookup_type))
1432 inline bool apply_string (hb_font_t *font,
1433 hb_buffer_t *buffer,
1434 hb_mask_t mask) const
1438 if (unlikely (!buffer->len))
1442 while (buffer->i < buffer->len)
1444 if ((buffer->info[buffer->i].mask & mask) &&
1445 apply_once (font, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
1454 inline bool sanitize (hb_sanitize_context_t *c) {
1456 if (unlikely (!Lookup::sanitize (c))) return false;
1457 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1458 return list.sanitize (c, this, get_type ());
1462 typedef OffsetListOf<PosLookup> PosLookupList;
1468 struct GPOS : GSUBGPOS
1470 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1472 inline const PosLookup& get_lookup (unsigned int i) const
1473 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1475 inline bool position_lookup (hb_font_t *font,
1476 hb_buffer_t *buffer,
1477 unsigned int lookup_index,
1478 hb_mask_t mask) const
1479 { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
1481 static inline void position_finish (hb_buffer_t *buffer);
1483 inline bool sanitize (hb_sanitize_context_t *c) {
1485 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1486 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1487 return list.sanitize (c, this);
1490 DEFINE_SIZE_STATIC (10);
1494 GPOS::position_finish (hb_buffer_t *buffer)
1498 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
1499 hb_direction_t direction = buffer->props.direction;
1501 /* Handle cursive connections:
1502 * First handle all chain-back connections, then handle all chain-forward connections. */
1503 if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
1505 for (j = 0; j < len; j++) {
1506 if (pos[j].cursive_chain() < 0)
1507 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
1509 for (i = len; i > 0; i--) {
1511 if (pos[j].cursive_chain() > 0)
1512 pos[j].y_offset += pos[j + pos[j].cursive_chain()].y_offset;
1517 for (j = 0; j < len; j++) {
1518 if (pos[j].cursive_chain() < 0)
1519 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
1521 for (i = len; i > 0; i--) {
1523 if (pos[j].cursive_chain() > 0)
1524 pos[j].x_offset += pos[j + pos[j].cursive_chain()].x_offset;
1529 /* Handle attachments */
1530 for (i = 0; i < len; i++)
1531 if (pos[i].attach_lookback())
1533 unsigned int back = i - pos[i].attach_lookback();
1534 pos[i].x_offset += pos[back].x_offset;
1535 pos[i].y_offset += pos[back].y_offset;
1537 if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
1538 for (j = back + 1; j < i + 1; j++) {
1539 pos[i].x_offset += pos[j].x_advance;
1540 pos[i].y_offset += pos[j].y_advance;
1543 for (j = back; j < i; j++) {
1544 pos[i].x_offset -= pos[j].x_advance;
1545 pos[i].y_offset -= pos[j].y_advance;
1551 /* Out-of-class implementation for methods recursing */
1553 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1556 return get_subtable ().apply (c, get_type ());
1559 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1562 if (unlikely (!Extension::sanitize (c))) return false;
1563 unsigned int offset = get_offset ();
1564 if (unlikely (!offset)) return true;
1565 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1568 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1570 const GPOS &gpos = *(c->face->ot_layout->gpos);
1571 const PosLookup &l = gpos.get_lookup (lookup_index);
1573 if (unlikely (c->nesting_level_left == 0))
1576 if (unlikely (c->context_length < 1))
1579 return l.apply_once (c->font, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
1583 #undef attach_lookback
1584 #undef cursive_chain
1589 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */