2 * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Red Hat Author(s): Behdad Esfahbod
27 #ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
28 #define HB_OT_LAYOUT_GPOS_PRIVATE_HH
30 #include "hb-ot-layout-gsubgpos-private.hh"
34 #define BUFFER c->buffer
37 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
39 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
43 typedef Value ValueRecord[VAR];
45 struct ValueFormat : USHORT
49 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
50 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
51 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
52 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
53 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
54 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
55 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
56 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
57 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
58 reserved = 0xF000, /* For future use */
60 devices = 0x00F0 /* Mask for having any Device table */
63 /* All fields are options. Only those available advance the value pointer. */
65 SHORT xPlacement; /* Horizontal adjustment for
66 * placement--in design units */
67 SHORT yPlacement; /* Vertical adjustment for
68 * placement--in design units */
69 SHORT xAdvance; /* Horizontal adjustment for
70 * advance--in design units (only used
71 * for horizontal writing) */
72 SHORT yAdvance; /* Vertical adjustment for advance--in
73 * design units (only used for vertical
75 Offset xPlaDevice; /* Offset to Device table for
76 * horizontal placement--measured from
77 * beginning of PosTable (may be NULL) */
78 Offset yPlaDevice; /* Offset to Device table for vertical
79 * placement--measured from beginning
80 * of PosTable (may be NULL) */
81 Offset xAdvDevice; /* Offset to Device table for
82 * horizontal advance--measured from
83 * beginning of PosTable (may be NULL) */
84 Offset yAdvDevice; /* Offset to Device table for vertical
85 * advance--measured from beginning of
86 * PosTable (may be NULL) */
89 inline unsigned int get_len () const
90 { return _hb_popcount32 ((unsigned int) *this); }
91 inline unsigned int get_size () const
92 { return get_len () * Value::static_size; }
94 void apply_value (hb_ot_layout_context_t *layout,
97 hb_internal_glyph_position_t &glyph_pos) const
99 unsigned int x_ppem, y_ppem;
100 hb_16dot16_t x_scale, y_scale;
101 unsigned int format = *this;
105 x_scale = layout->font->x_scale;
106 y_scale = layout->font->y_scale;
107 /* design units -> fractional pixel */
108 if (format & xPlacement) glyph_pos.x_offset += _hb_16dot16_mul_round (x_scale, get_short (values++));
109 if (format & yPlacement) glyph_pos.y_offset += _hb_16dot16_mul_round (y_scale, get_short (values++));
110 if (format & xAdvance) glyph_pos.x_advance += _hb_16dot16_mul_round (x_scale, get_short (values++));
111 if (format & yAdvance) glyph_pos.y_advance += _hb_16dot16_mul_round (y_scale, get_short (values++));
113 if (!has_device ()) return;
115 x_ppem = layout->font->x_ppem;
116 y_ppem = layout->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_delta (x_ppem) << 16; else values++;
124 if (format & yPlaDevice) {
125 if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_delta (y_ppem) << 16; else values++;
127 if (format & xAdvDevice) {
128 if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_delta (x_ppem) << 16; else values++;
130 if (format & yAdvDevice) {
131 if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_delta (y_ppem) << 16; 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 () 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_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
213 hb_position_t *x, hb_position_t *y) const
215 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
216 *y = _hb_16dot16_mul_round (layout->font->y_scale, 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_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
238 hb_position_t *x, hb_position_t *y) const
240 unsigned int x_ppem = layout->font->x_ppem;
241 unsigned int y_ppem = layout->font->y_ppem;
242 hb_position_t cx, cy;
245 if (x_ppem || y_ppem)
246 ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
247 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
248 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout->font->y_scale, 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_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
271 hb_position_t *x, hb_position_t *y) const
273 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
274 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
276 /* pixel -> fractional pixel */
277 if (layout->font->x_ppem)
278 *x += (this+xDeviceTable).get_delta (layout->font->x_ppem) << 16;
279 if (layout->font->y_ppem)
280 *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) << 16;
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_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
309 hb_position_t *x, hb_position_t *y) const
313 case 1: u.format1.get_anchor (layout, glyph_id, x, y); return;
314 case 2: u.format2.get_anchor (layout, glyph_id, x, y); return;
315 case 3: u.format3.get_anchor (layout, 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 (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->layout, c->buffer->in_string[c->buffer->in_pos].codepoint, &mark_x, &mark_y);
407 glyph_anchor.get_anchor (c->layout, c->buffer->in_string[glyph_pos].codepoint, &base_x, &base_y);
409 hb_internal_glyph_position_t &o = c->buffer->positions[c->buffer->in_pos];
412 o.x_offset = base_x - mark_x;
413 o.y_offset = base_y - mark_y;
414 o.back = c->buffer->in_pos - glyph_pos;
420 inline bool sanitize (hb_sanitize_context_t *c) {
422 return ArrayOf<MarkRecord>::sanitize (c, this);
429 struct SinglePosFormat1
431 friend struct SinglePos;
434 inline bool apply (hb_apply_context_t *c) const
437 unsigned int index = (this+coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
438 if (likely (index == NOT_COVERED))
441 valueFormat.apply_value (c->layout, this, values, c->buffer->positions[c->buffer->in_pos]);
447 inline bool sanitize (hb_sanitize_context_t *c) {
449 return c->check_struct (this)
450 && coverage.sanitize (c, this)
451 && valueFormat.sanitize_value (c, this, values);
455 USHORT format; /* Format identifier--format = 1 */
457 coverage; /* Offset to Coverage table--from
458 * beginning of subtable */
459 ValueFormat valueFormat; /* Defines the types of data in the
461 ValueRecord values; /* Defines positioning
462 * value(s)--applied to all glyphs in
463 * the Coverage table */
465 DEFINE_SIZE_ARRAY (6, values);
468 struct SinglePosFormat2
470 friend struct SinglePos;
473 inline bool apply (hb_apply_context_t *c) const
476 unsigned int index = (this+coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
477 if (likely (index == NOT_COVERED))
480 if (likely (index >= valueCount))
483 valueFormat.apply_value (c->layout, this,
484 &values[index * valueFormat.get_len ()],
485 c->buffer->positions[c->buffer->in_pos]);
491 inline bool sanitize (hb_sanitize_context_t *c) {
493 return c->check_struct (this)
494 && coverage.sanitize (c, this)
495 && valueFormat.sanitize_values (c, this, values, valueCount);
499 USHORT format; /* Format identifier--format = 2 */
501 coverage; /* Offset to Coverage table--from
502 * beginning of subtable */
503 ValueFormat valueFormat; /* Defines the types of data in the
505 USHORT valueCount; /* Number of ValueRecords */
506 ValueRecord values; /* Array of ValueRecords--positioning
507 * values applied to glyphs */
509 DEFINE_SIZE_ARRAY (8, values);
514 friend struct PosLookupSubTable;
517 inline bool apply (hb_apply_context_t *c) const
521 case 1: return u.format1.apply (c);
522 case 2: return u.format2.apply (c);
523 default:return false;
527 inline bool sanitize (hb_sanitize_context_t *c) {
529 if (!u.format.sanitize (c)) return false;
531 case 1: return u.format1.sanitize (c);
532 case 2: return u.format2.sanitize (c);
539 USHORT format; /* Format identifier */
540 SinglePosFormat1 format1;
541 SinglePosFormat2 format2;
546 struct PairValueRecord
548 friend struct PairSet;
551 GlyphID secondGlyph; /* GlyphID of second glyph in the
552 * pair--first glyph is listed in the
554 ValueRecord values; /* Positioning data for the first glyph
555 * followed by for second glyph */
557 DEFINE_SIZE_ARRAY (2, values);
562 friend struct PairPosFormat1;
564 inline bool apply (hb_apply_context_t *c,
565 const ValueFormat *valueFormats,
566 unsigned int pos) const
569 unsigned int len1 = valueFormats[0].get_len ();
570 unsigned int len2 = valueFormats[1].get_len ();
571 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
573 unsigned int count = len;
574 const PairValueRecord *record = CastP<PairValueRecord> (array);
575 for (unsigned int i = 0; i < count; i++)
577 if (c->buffer->in_string[pos].codepoint == record->secondGlyph)
579 valueFormats[0].apply_value (c->layout, this, &record->values[0], c->buffer->positions[c->buffer->in_pos]);
580 valueFormats[1].apply_value (c->layout, this, &record->values[len1], c->buffer->positions[pos]);
583 c->buffer->in_pos = pos;
586 record = &StructAtOffset<PairValueRecord> (record, record_size);
592 struct sanitize_closure_t {
594 ValueFormat *valueFormats;
595 unsigned int len1; /* valueFormats[0].get_len() */
596 unsigned int stride; /* 1 + len1 + len2 */
599 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
601 if (!(c->check_struct (this)
602 && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
604 unsigned int count = len;
605 PairValueRecord *record = CastP<PairValueRecord> (array);
606 return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
607 && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
611 USHORT len; /* Number of PairValueRecords */
612 USHORT array[VAR]; /* Array of PairValueRecords--ordered
613 * by GlyphID of the second glyph */
615 DEFINE_SIZE_ARRAY (2, array);
618 struct PairPosFormat1
620 friend struct PairPos;
623 inline bool apply (hb_apply_context_t *c) const
626 unsigned int end = MIN (c->buffer->in_length, c->buffer->in_pos + c->context_length);
627 if (unlikely (c->buffer->in_pos + 2 > end))
630 unsigned int index = (this+coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
631 if (likely (index == NOT_COVERED))
634 unsigned int j = c->buffer->in_pos + 1;
635 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->in_string[j], c->lookup_flag, NULL))
637 if (unlikely (j == end))
642 return (this+pairSet[index]).apply (c, &valueFormat1, j);
645 inline bool sanitize (hb_sanitize_context_t *c) {
648 unsigned int len1 = valueFormat1.get_len ();
649 unsigned int len2 = valueFormat2.get_len ();
650 PairSet::sanitize_closure_t closure = {
657 return c->check_struct (this)
658 && coverage.sanitize (c, this)
659 && pairSet.sanitize (c, this, &closure);
663 USHORT format; /* Format identifier--format = 1 */
665 coverage; /* Offset to Coverage table--from
666 * beginning of subtable */
667 ValueFormat valueFormat1; /* Defines the types of data in
668 * ValueRecord1--for the first glyph
669 * in the pair--may be zero (0) */
670 ValueFormat valueFormat2; /* Defines the types of data in
671 * ValueRecord2--for the second glyph
672 * in the pair--may be zero (0) */
673 OffsetArrayOf<PairSet>
674 pairSet; /* Array of PairSet tables
675 * ordered by Coverage Index */
677 DEFINE_SIZE_ARRAY (10, pairSet);
680 struct PairPosFormat2
682 friend struct PairPos;
685 inline bool apply (hb_apply_context_t *c) const
688 unsigned int end = MIN (c->buffer->in_length, c->buffer->in_pos + c->context_length);
689 if (unlikely (c->buffer->in_pos + 2 > end))
692 unsigned int index = (this+coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
693 if (likely (index == NOT_COVERED))
696 unsigned int j = c->buffer->in_pos + 1;
697 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->in_string[j], c->lookup_flag, NULL))
699 if (unlikely (j == end))
704 unsigned int len1 = valueFormat1.get_len ();
705 unsigned int len2 = valueFormat2.get_len ();
706 unsigned int record_len = len1 + len2;
708 unsigned int klass1 = (this+classDef1) (c->buffer->in_string[c->buffer->in_pos].codepoint);
709 unsigned int klass2 = (this+classDef2) (c->buffer->in_string[j].codepoint);
710 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
713 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
714 valueFormat1.apply_value (c->layout, this, v, c->buffer->positions[c->buffer->in_pos]);
715 valueFormat2.apply_value (c->layout, this, v + len1, c->buffer->positions[j]);
719 c->buffer->in_pos = j;
724 inline bool sanitize (hb_sanitize_context_t *c) {
726 if (!(c->check_struct (this)
727 && coverage.sanitize (c, this)
728 && classDef1.sanitize (c, this)
729 && classDef2.sanitize (c, this))) return false;
731 unsigned int len1 = valueFormat1.get_len ();
732 unsigned int len2 = valueFormat2.get_len ();
733 unsigned int stride = len1 + len2;
734 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
735 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
736 return c->check_array (values, record_size, count) &&
737 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
738 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
742 USHORT format; /* Format identifier--format = 2 */
744 coverage; /* Offset to Coverage table--from
745 * beginning of subtable */
746 ValueFormat valueFormat1; /* ValueRecord definition--for the
747 * first glyph of the pair--may be zero
749 ValueFormat valueFormat2; /* ValueRecord definition--for the
750 * second glyph of the pair--may be
753 classDef1; /* Offset to ClassDef table--from
754 * beginning of PairPos subtable--for
755 * the first glyph of the pair */
757 classDef2; /* Offset to ClassDef table--from
758 * beginning of PairPos subtable--for
759 * the second glyph of the pair */
760 USHORT class1Count; /* Number of classes in ClassDef1
761 * table--includes Class0 */
762 USHORT class2Count; /* Number of classes in ClassDef2
763 * table--includes Class0 */
764 ValueRecord values; /* Matrix of value pairs:
765 * class1-major, class2-minor,
766 * Each entry has value1 and value2 */
768 DEFINE_SIZE_ARRAY (16, values);
773 friend struct PosLookupSubTable;
776 inline bool apply (hb_apply_context_t *c) const
780 case 1: return u.format1.apply (c);
781 case 2: return u.format2.apply (c);
782 default:return false;
786 inline bool sanitize (hb_sanitize_context_t *c) {
788 if (!u.format.sanitize (c)) return false;
790 case 1: return u.format1.sanitize (c);
791 case 2: return u.format2.sanitize (c);
798 USHORT format; /* Format identifier */
799 PairPosFormat1 format1;
800 PairPosFormat2 format2;
805 struct EntryExitRecord
807 friend struct CursivePosFormat1;
809 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
811 return entryAnchor.sanitize (c, base)
812 && exitAnchor.sanitize (c, base);
817 entryAnchor; /* Offset to EntryAnchor table--from
818 * beginning of CursivePos
819 * subtable--may be NULL */
821 exitAnchor; /* Offset to ExitAnchor table--from
822 * beginning of CursivePos
823 * subtable--may be NULL */
825 DEFINE_SIZE_STATIC (4);
828 struct CursivePosFormat1
830 friend struct CursivePos;
833 inline bool apply (hb_apply_context_t *c) const
836 /* Now comes the messiest part of the whole OpenType
837 specification. At first glance, cursive connections seem easy
838 to understand, but there are pitfalls! The reason is that
839 the specs don't mention how to compute the advance values
840 resp. glyph offsets. I was told it would be an omission, to
841 be fixed in the next OpenType version... Again many thanks to
842 Andrei Burago <andreib@microsoft.com> for clarifications.
844 Consider the following example:
857 glyph1: advance width = 12
860 glyph2: advance width = 11
863 LSB is 1 for both glyphs (so the boxes drawn above are glyph
864 bboxes). Writing direction is R2L; `0' denotes the glyph's
867 Now the surprising part: The advance width of the *left* glyph
868 (resp. of the *bottom* glyph) will be modified, no matter
869 whether the writing direction is L2R or R2L (resp. T2B or
870 B2T)! This assymetry is caused by the fact that the glyph's
871 coordinate origin is always the lower left corner for all
874 Continuing the above example, we can compute the new
875 (horizontal) advance width of glyph2 as
879 and the new vertical offset of glyph2 as
884 Vertical writing direction is far more complicated:
886 a) Assuming that we recompute the advance height of the lower glyph:
893 yadv2 | 0+--+------+ -- BSB1 --
896 BSB2 -- 0+--------+ --
899 glyph1: advance height = 6
902 glyph2: advance height = 7
905 TSB is 1 for both glyphs; writing direction is T2B.
908 BSB1 = yadv1 - (TSB1 + ymax1)
909 BSB2 = yadv2 - (TSB2 + ymax2)
912 vertical advance width of glyph2
913 = y_offset + BSB2 - BSB1
914 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
915 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
916 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
919 b) Assuming that we recompute the advance height of the upper glyph:
924 TSB2 -- +-----+--+ 1 | yadv1 ymax1
926 yadv2 | 0+--+------+ -- --
927 ymax2 | 2 | -- y_offset
932 glyph1: advance height = 6
935 glyph2: advance height = 7
938 TSB is 1 for both glyphs; writing direction is T2B.
942 vertical advance width of glyph2
943 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
944 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
947 Comparing a) with b) shows that b) is easier to compute. I'll wait
948 for a reply from Andrei to see what should really be implemented...
950 Since horizontal advance widths or vertical advance heights
951 can be used alone but not together, no ambiguity occurs. */
953 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &c->layout->info.gpos;
954 hb_codepoint_t last_pos = gpi->last;
955 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
957 /* We don't handle mark glyphs here. */
958 if (c->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
961 unsigned int index = (this+coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
962 if (likely (index == NOT_COVERED))
965 const EntryExitRecord &record = entryExitRecord[index];
967 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
970 hb_position_t entry_x, entry_y;
971 (this+record.entryAnchor).get_anchor (c->layout, c->buffer->in_string[c->buffer->in_pos].codepoint, &entry_x, &entry_y);
975 if (c->buffer->direction == HB_DIRECTION_RTL)
977 /* advance is absolute, not relative */
978 c->buffer->positions[c->buffer->in_pos].x_advance = entry_x - gpi->anchor_x;
982 /* advance is absolute, not relative */
983 c->buffer->positions[last_pos].x_advance = gpi->anchor_x - entry_x;
986 if (c->lookup_flag & LookupFlag::RightToLeft)
988 c->buffer->positions[last_pos].cursive_chain = last_pos - c->buffer->in_pos;
989 c->buffer->positions[last_pos].y_offset = entry_y - gpi->anchor_y;
993 c->buffer->positions[c->buffer->in_pos].cursive_chain = c->buffer->in_pos - last_pos;
994 c->buffer->positions[c->buffer->in_pos].y_offset = gpi->anchor_y - entry_y;
998 if (record.exitAnchor)
1000 gpi->last = c->buffer->in_pos;
1001 (this+record.exitAnchor).get_anchor (c->layout, c->buffer->in_string[c->buffer->in_pos].codepoint, &gpi->anchor_x, &gpi->anchor_y);
1004 c->buffer->in_pos++;
1008 inline bool sanitize (hb_sanitize_context_t *c) {
1010 return coverage.sanitize (c, this)
1011 && entryExitRecord.sanitize (c, this);
1015 USHORT format; /* Format identifier--format = 1 */
1017 coverage; /* Offset to Coverage table--from
1018 * beginning of subtable */
1019 ArrayOf<EntryExitRecord>
1020 entryExitRecord; /* Array of EntryExit records--in
1021 * Coverage Index order */
1023 DEFINE_SIZE_ARRAY (6, entryExitRecord);
1028 friend struct PosLookupSubTable;
1031 inline bool apply (hb_apply_context_t *c) const
1035 case 1: return u.format1.apply (c);
1036 default:return false;
1040 inline bool sanitize (hb_sanitize_context_t *c) {
1042 if (!u.format.sanitize (c)) return false;
1044 case 1: return u.format1.sanitize (c);
1045 default:return true;
1051 USHORT format; /* Format identifier */
1052 CursivePosFormat1 format1;
1057 typedef AnchorMatrix BaseArray; /* base-major--
1058 * in order of BaseCoverage Index--,
1060 * ordered by class--zero-based. */
1062 struct MarkBasePosFormat1
1064 friend struct MarkBasePos;
1067 inline bool apply (hb_apply_context_t *c) const
1070 unsigned int mark_index = (this+markCoverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
1071 if (likely (mark_index == NOT_COVERED))
1074 /* now we search backwards for a non-mark glyph */
1075 unsigned int property;
1076 unsigned int j = c->buffer->in_pos;
1082 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->in_string[j], LookupFlag::IgnoreMarks, &property));
1084 /* The following assertion is too strong, so we've disabled it. */
1085 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1088 unsigned int base_index = (this+baseCoverage) (c->buffer->in_string[j].codepoint);
1089 if (base_index == NOT_COVERED)
1092 return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
1095 inline bool sanitize (hb_sanitize_context_t *c) {
1097 return c->check_struct (this)
1098 && markCoverage.sanitize (c, this)
1099 && baseCoverage.sanitize (c, this)
1100 && markArray.sanitize (c, this)
1101 && baseArray.sanitize (c, this, (unsigned int) classCount);
1105 USHORT format; /* Format identifier--format = 1 */
1107 markCoverage; /* Offset to MarkCoverage table--from
1108 * beginning of MarkBasePos subtable */
1110 baseCoverage; /* Offset to BaseCoverage table--from
1111 * beginning of MarkBasePos subtable */
1112 USHORT classCount; /* Number of classes defined for marks */
1114 markArray; /* Offset to MarkArray table--from
1115 * beginning of MarkBasePos subtable */
1117 baseArray; /* Offset to BaseArray table--from
1118 * beginning of MarkBasePos subtable */
1120 DEFINE_SIZE_STATIC (12);
1125 friend struct PosLookupSubTable;
1128 inline bool apply (hb_apply_context_t *c) const
1132 case 1: return u.format1.apply (c);
1133 default:return false;
1137 inline bool sanitize (hb_sanitize_context_t *c) {
1139 if (!u.format.sanitize (c)) return false;
1141 case 1: return u.format1.sanitize (c);
1142 default:return true;
1148 USHORT format; /* Format identifier */
1149 MarkBasePosFormat1 format1;
1154 typedef AnchorMatrix LigatureAttach; /* component-major--
1155 * in order of writing direction--,
1157 * ordered by class--zero-based. */
1159 typedef OffsetListOf<LigatureAttach> LigatureArray;
1160 /* Array of LigatureAttach
1162 * LigatureCoverage Index */
1164 struct MarkLigPosFormat1
1166 friend struct MarkLigPos;
1169 inline bool apply (hb_apply_context_t *c) const
1172 unsigned int mark_index = (this+markCoverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
1173 if (likely (mark_index == NOT_COVERED))
1176 /* now we search backwards for a non-mark glyph */
1177 unsigned int property;
1178 unsigned int j = c->buffer->in_pos;
1184 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->in_string[j], LookupFlag::IgnoreMarks, &property));
1186 /* The following assertion is too strong, so we've disabled it. */
1187 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1190 unsigned int lig_index = (this+ligatureCoverage) (c->buffer->in_string[j].codepoint);
1191 if (lig_index == NOT_COVERED)
1194 const LigatureArray& lig_array = this+ligatureArray;
1195 const LigatureAttach& lig_attach = lig_array[lig_index];
1197 /* Find component to attach to */
1198 unsigned int comp_count = lig_attach.rows;
1199 if (unlikely (!comp_count))
1201 unsigned int comp_index;
1202 /* We must now check whether the ligature ID of the current mark glyph
1203 * is identical to the ligature ID of the found ligature. If yes, we
1204 * can directly use the component index. If not, we attach the mark
1205 * glyph to the last component of the ligature. */
1206 if (c->buffer->in_string[j].lig_id && c->buffer->in_string[j].lig_id == c->buffer->in_string[c->buffer->in_pos].lig_id && c->buffer->in_string[c->buffer->in_pos].component)
1208 comp_index = c->buffer->in_string[c->buffer->in_pos].component - 1;
1209 if (comp_index >= comp_count)
1210 comp_index = comp_count - 1;
1213 comp_index = comp_count - 1;
1215 return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1218 inline bool sanitize (hb_sanitize_context_t *c) {
1220 return c->check_struct (this)
1221 && markCoverage.sanitize (c, this)
1222 && ligatureCoverage.sanitize (c, this)
1223 && markArray.sanitize (c, this)
1224 && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1228 USHORT format; /* Format identifier--format = 1 */
1230 markCoverage; /* Offset to Mark Coverage table--from
1231 * beginning of MarkLigPos subtable */
1233 ligatureCoverage; /* Offset to Ligature Coverage
1234 * table--from beginning of MarkLigPos
1236 USHORT classCount; /* Number of defined mark classes */
1238 markArray; /* Offset to MarkArray table--from
1239 * beginning of MarkLigPos subtable */
1240 OffsetTo<LigatureArray>
1241 ligatureArray; /* Offset to LigatureArray table--from
1242 * beginning of MarkLigPos subtable */
1244 DEFINE_SIZE_STATIC (12);
1249 friend struct PosLookupSubTable;
1252 inline bool apply (hb_apply_context_t *c) const
1256 case 1: return u.format1.apply (c);
1257 default:return false;
1261 inline bool sanitize (hb_sanitize_context_t *c) {
1263 if (!u.format.sanitize (c)) return false;
1265 case 1: return u.format1.sanitize (c);
1266 default:return true;
1272 USHORT format; /* Format identifier */
1273 MarkLigPosFormat1 format1;
1278 typedef AnchorMatrix Mark2Array; /* mark2-major--
1279 * in order of Mark2Coverage Index--,
1281 * ordered by class--zero-based. */
1283 struct MarkMarkPosFormat1
1285 friend struct MarkMarkPos;
1288 inline bool apply (hb_apply_context_t *c) const
1291 unsigned int mark1_index = (this+mark1Coverage) (c->buffer->in_string[c->buffer->in_pos].codepoint);
1292 if (likely (mark1_index == NOT_COVERED))
1295 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1296 unsigned int property;
1297 unsigned int j = c->buffer->in_pos;
1303 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->in_string[j], c->lookup_flag, &property));
1305 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1308 /* Two marks match only if they belong to the same base, or same component
1309 * of the same ligature. That is, the component numbers must match, and
1310 * if those are non-zero, the ligid number should also match. */
1311 if ((c->buffer->in_string[j].component != c->buffer->in_string[c->buffer->in_pos].component) ||
1312 (c->buffer->in_string[j].component && c->buffer->in_string[j].lig_id != c->buffer->in_string[c->buffer->in_pos].lig_id))
1315 unsigned int mark2_index = (this+mark2Coverage) (c->buffer->in_string[j].codepoint);
1316 if (mark2_index == NOT_COVERED)
1319 return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1322 inline bool sanitize (hb_sanitize_context_t *c) {
1324 return c->check_struct (this)
1325 && mark1Coverage.sanitize (c, this)
1326 && mark2Coverage.sanitize (c, this)
1327 && mark1Array.sanitize (c, this)
1328 && mark2Array.sanitize (c, this, (unsigned int) classCount);
1332 USHORT format; /* Format identifier--format = 1 */
1334 mark1Coverage; /* Offset to Combining Mark1 Coverage
1335 * table--from beginning of MarkMarkPos
1338 mark2Coverage; /* Offset to Combining Mark2 Coverage
1339 * table--from beginning of MarkMarkPos
1341 USHORT classCount; /* Number of defined mark classes */
1343 mark1Array; /* Offset to Mark1Array table--from
1344 * beginning of MarkMarkPos subtable */
1345 OffsetTo<Mark2Array>
1346 mark2Array; /* Offset to Mark2Array table--from
1347 * beginning of MarkMarkPos subtable */
1349 DEFINE_SIZE_STATIC (12);
1354 friend struct PosLookupSubTable;
1357 inline bool apply (hb_apply_context_t *c) const
1361 case 1: return u.format1.apply (c);
1362 default:return false;
1366 inline bool sanitize (hb_sanitize_context_t *c) {
1368 if (!u.format.sanitize (c)) return false;
1370 case 1: return u.format1.sanitize (c);
1371 default:return true;
1377 USHORT format; /* Format identifier */
1378 MarkMarkPosFormat1 format1;
1383 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1385 struct ContextPos : Context
1387 friend struct PosLookupSubTable;
1390 inline bool apply (hb_apply_context_t *c) const
1393 return Context::apply (c, position_lookup);
1397 struct ChainContextPos : ChainContext
1399 friend struct PosLookupSubTable;
1402 inline bool apply (hb_apply_context_t *c) const
1405 return ChainContext::apply (c, position_lookup);
1410 struct ExtensionPos : Extension
1412 friend struct PosLookupSubTable;
1415 inline const struct PosLookupSubTable& get_subtable (void) const
1417 unsigned int offset = get_offset ();
1418 if (unlikely (!offset)) return Null(PosLookupSubTable);
1419 return StructAtOffset<PosLookupSubTable> (this, offset);
1422 inline bool apply (hb_apply_context_t *c) const;
1424 inline bool sanitize (hb_sanitize_context_t *c);
1434 struct PosLookupSubTable
1436 friend struct PosLookup;
1450 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1453 switch (lookup_type) {
1454 case Single: return u.single.apply (c);
1455 case Pair: return u.pair.apply (c);
1456 case Cursive: return u.cursive.apply (c);
1457 case MarkBase: return u.markBase.apply (c);
1458 case MarkLig: return u.markLig.apply (c);
1459 case MarkMark: return u.markMark.apply (c);
1460 case Context: return u.c.apply (c);
1461 case ChainContext: return u.chainContext.apply (c);
1462 case Extension: return u.extension.apply (c);
1463 default:return false;
1467 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1469 switch (lookup_type) {
1470 case Single: return u.single.sanitize (c);
1471 case Pair: return u.pair.sanitize (c);
1472 case Cursive: return u.cursive.sanitize (c);
1473 case MarkBase: return u.markBase.sanitize (c);
1474 case MarkLig: return u.markLig.sanitize (c);
1475 case MarkMark: return u.markMark.sanitize (c);
1476 case Context: return u.c.sanitize (c);
1477 case ChainContext: return u.chainContext.sanitize (c);
1478 case Extension: return u.extension.sanitize (c);
1479 default:return true;
1489 MarkBasePos markBase;
1491 MarkMarkPos markMark;
1493 ChainContextPos chainContext;
1494 ExtensionPos extension;
1497 DEFINE_SIZE_UNION (2, sub_format);
1501 struct PosLookup : Lookup
1503 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1504 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1506 inline bool apply_once (hb_ot_layout_context_t *layout,
1507 hb_buffer_t *buffer,
1508 unsigned int context_length,
1509 unsigned int nesting_level_left) const
1511 unsigned int lookup_type = get_type ();
1512 hb_apply_context_t c[1] = {{0}};
1516 c->context_length = context_length;
1517 c->nesting_level_left = nesting_level_left;
1518 c->lookup_flag = get_flag ();
1520 if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->in_string[c->buffer->in_pos], c->lookup_flag, &c->property))
1523 for (unsigned int i = 0; i < get_subtable_count (); i++)
1524 if (get_subtable (i).apply (c, lookup_type))
1530 inline bool apply_string (hb_ot_layout_context_t *layout,
1531 hb_buffer_t *buffer,
1532 hb_mask_t mask) const
1535 #define BUFFER buffer
1538 if (unlikely (!buffer->in_length))
1541 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1544 while (buffer->in_pos < buffer->in_length)
1547 if (~buffer->in_string[buffer->in_pos].mask & mask)
1549 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1555 /* Contrary to properties defined in GDEF, user-defined properties
1556 will always stop a possible cursive positioning. */
1557 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1567 inline bool sanitize (hb_sanitize_context_t *c) {
1569 if (unlikely (!Lookup::sanitize (c))) return false;
1570 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1571 return list.sanitize (c, this, get_type ());
1575 typedef OffsetListOf<PosLookup> PosLookupList;
1581 struct GPOS : GSUBGPOS
1583 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1585 inline const PosLookup& get_lookup (unsigned int i) const
1586 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1588 inline bool position_lookup (hb_ot_layout_context_t *layout,
1589 hb_buffer_t *buffer,
1590 unsigned int lookup_index,
1591 hb_mask_t mask) const
1592 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1594 inline bool sanitize (hb_sanitize_context_t *c) {
1596 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1597 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1598 return list.sanitize (c, this);
1601 DEFINE_SIZE_STATIC (10);
1605 /* Out-of-class implementation for methods recursing */
1607 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1610 return get_subtable ().apply (c, get_type ());
1613 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1616 if (unlikely (!Extension::sanitize (c))) return false;
1617 unsigned int offset = get_offset ();
1618 if (unlikely (!offset)) return true;
1619 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1622 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1624 const GPOS &gpos = *(c->layout->face->ot_layout.gpos);
1625 const PosLookup &l = gpos.get_lookup (lookup_index);
1627 if (unlikely (c->nesting_level_left == 0))
1630 if (unlikely (c->context_length < 1))
1633 return l.apply_once (c->layout, c->buffer, c->context_length, c->nesting_level_left - 1);
1637 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */