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 context->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 *context, 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 (context, base)) return false;
145 if ((format & yPlaDevice) && !get_device (values++).sanitize (context, base)) return false;
146 if ((format & xAdvDevice) && !get_device (values++).sanitize (context, base)) return false;
147 if ((format & yAdvDevice) && !get_device (values++).sanitize (context, 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 *context, void *base, Value *values) {
169 return context->check_range (values, get_size ())
170 && (!has_device () || sanitize_value_devices (context, base, values));
173 inline bool sanitize_values (hb_sanitize_context_t *context, void *base, Value *values, unsigned int count) {
175 unsigned int len = get_len ();
177 if (!context->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 (context, 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 *context, 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 (context, 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 *context) {
221 return context->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 *context) {
253 return context->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 *context) {
285 return context->check_struct (this)
286 && xDeviceTable.sanitize (context, this)
287 && yDeviceTable.sanitize (context, 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 *context) {
322 if (!u.format.sanitize (context)) return false;
324 case 1: return u.format1.sanitize (context);
325 case 2: return u.format2.sanitize (context);
326 case 3: return u.format3.sanitize (context);
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 *context, unsigned int cols) {
352 if (!context->check_struct (this)) return false;
353 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
354 unsigned int count = rows * cols;
355 if (!context->check_array (matrix, matrix[0].static_size, count)) return false;
356 for (unsigned int i = 0; i < count; i++)
357 if (!matrix[i].sanitize (context, 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 *context, void *base) {
377 return context->check_struct (this)
378 && markAnchor.sanitize (context, 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 *context,
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 (context->layout, IN_CURGLYPH (), &mark_x, &mark_y);
407 glyph_anchor.get_anchor (context->layout, IN_GLYPH (glyph_pos), &base_x, &base_y);
409 hb_internal_glyph_position_t *o = POSITION (context->buffer->in_pos);
412 o->x_offset = base_x - mark_x;
413 o->y_offset = base_y - mark_y;
414 o->back = context->buffer->in_pos - glyph_pos;
416 context->buffer->in_pos++;
420 inline bool sanitize (hb_sanitize_context_t *context) {
422 return ArrayOf<MarkRecord>::sanitize (context, this);
429 struct SinglePosFormat1
431 friend struct SinglePos;
434 inline bool apply (hb_apply_context_t *context) const
437 unsigned int index = (this+coverage) (IN_CURGLYPH ());
438 if (likely (index == NOT_COVERED))
441 valueFormat.apply_value (context->layout, this, values, CURPOSITION ());
443 context->buffer->in_pos++;
447 inline bool sanitize (hb_sanitize_context_t *context) {
449 return context->check_struct (this)
450 && coverage.sanitize (context, this)
451 && valueFormat.sanitize_value (context, 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 *context) const
476 unsigned int index = (this+coverage) (IN_CURGLYPH ());
477 if (likely (index == NOT_COVERED))
480 if (likely (index >= valueCount))
483 valueFormat.apply_value (context->layout, this,
484 &values[index * valueFormat.get_len ()],
487 context->buffer->in_pos++;
491 inline bool sanitize (hb_sanitize_context_t *context) {
493 return context->check_struct (this)
494 && coverage.sanitize (context, this)
495 && valueFormat.sanitize_values (context, 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 *context) const
521 case 1: return u.format1.apply (context);
522 case 2: return u.format2.apply (context);
523 default:return false;
527 inline bool sanitize (hb_sanitize_context_t *context) {
529 if (!u.format.sanitize (context)) return false;
531 case 1: return u.format1.sanitize (context);
532 case 2: return u.format2.sanitize (context);
539 USHORT format; /* Format identifier */
540 SinglePosFormat1 format1;
541 SinglePosFormat2 format2;
546 struct PairValueRecord
548 friend struct PairPosFormat1;
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 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
565 inline bool sanitize (hb_sanitize_context_t *context, unsigned int format_len) {
567 if (!context->check_struct (this)) return false;
568 unsigned int count = (1 + format_len) * len;
569 return context->check_array (array, USHORT::static_size, count);
573 USHORT len; /* Number of PairValueRecords */
574 USHORT array[VAR]; /* Array of PairValueRecords--ordered
575 * by GlyphID of the second glyph */
577 DEFINE_SIZE_ARRAY (2, array);
580 struct PairPosFormat1
582 friend struct PairPos;
585 inline bool apply (hb_apply_context_t *context) const
588 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
589 if (unlikely (context->buffer->in_pos + 2 > end))
592 unsigned int index = (this+coverage) (IN_CURGLYPH ());
593 if (likely (index == NOT_COVERED))
596 unsigned int j = context->buffer->in_pos + 1;
597 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
599 if (unlikely (j == end))
604 unsigned int len1 = valueFormat1.get_len ();
605 unsigned int len2 = valueFormat2.get_len ();
606 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
608 const PairSet &pair_set = this+pairSet[index];
609 unsigned int count = pair_set.len;
610 const PairValueRecord *record = CastP<PairValueRecord> (pair_set.array);
611 for (unsigned int i = 0; i < count; i++)
613 if (IN_GLYPH (j) == record->secondGlyph)
615 valueFormat1.apply_value (context->layout, this, &record->values[0], CURPOSITION ());
616 valueFormat2.apply_value (context->layout, this, &record->values[len1], POSITION (j));
619 context->buffer->in_pos = j;
622 record = &StructAtOffset<PairValueRecord> (record, record_size);
628 inline bool sanitize (hb_sanitize_context_t *context) {
631 unsigned int len1 = valueFormat1.get_len ();
632 unsigned int len2 = valueFormat2.get_len ();
634 if (!(context->check_struct (this)
635 && coverage.sanitize (context, this)
636 && likely (pairSet.sanitize (context, this, len1 + len2)))) return false;
638 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
640 unsigned int stride = 1 + len1 + len2;
641 unsigned int count1 = pairSet.len;
642 for (unsigned int i = 0; i < count1; i++)
644 PairSet &pair_set = const_cast<PairSet &> (this+pairSet[i]); /* XXX clean this up */
646 unsigned int count2 = pair_set.len;
647 PairValueRecord *record = CastP<PairValueRecord> (pair_set.array);
648 if (!(valueFormat1.sanitize_values_stride_unsafe (context, this, &record->values[0], count2, stride) &&
649 valueFormat2.sanitize_values_stride_unsafe (context, this, &record->values[len1], count2, stride)))
657 USHORT format; /* Format identifier--format = 1 */
659 coverage; /* Offset to Coverage table--from
660 * beginning of subtable */
661 ValueFormat valueFormat1; /* Defines the types of data in
662 * ValueRecord1--for the first glyph
663 * in the pair--may be zero (0) */
664 ValueFormat valueFormat2; /* Defines the types of data in
665 * ValueRecord2--for the second glyph
666 * in the pair--may be zero (0) */
667 OffsetArrayOf<PairSet>
668 pairSet; /* Array of PairSet tables
669 * ordered by Coverage Index */
671 DEFINE_SIZE_ARRAY (10, pairSet);
674 struct PairPosFormat2
676 friend struct PairPos;
679 inline bool apply (hb_apply_context_t *context) const
682 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
683 if (unlikely (context->buffer->in_pos + 2 > end))
686 unsigned int index = (this+coverage) (IN_CURGLYPH ());
687 if (likely (index == NOT_COVERED))
690 unsigned int j = context->buffer->in_pos + 1;
691 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
693 if (unlikely (j == end))
698 unsigned int len1 = valueFormat1.get_len ();
699 unsigned int len2 = valueFormat2.get_len ();
700 unsigned int record_len = len1 + len2;
702 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
703 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
704 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
707 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
708 valueFormat1.apply_value (context->layout, this, v, CURPOSITION ());
709 valueFormat2.apply_value (context->layout, this, v + len1, POSITION (j));
713 context->buffer->in_pos = j;
718 inline bool sanitize (hb_sanitize_context_t *context) {
720 if (!(context->check_struct (this)
721 && coverage.sanitize (context, this)
722 && classDef1.sanitize (context, this)
723 && classDef2.sanitize (context, this))) return false;
725 unsigned int len1 = valueFormat1.get_len ();
726 unsigned int len2 = valueFormat2.get_len ();
727 unsigned int stride = len1 + len2;
728 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
729 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
730 return context->check_array (values, record_size, count) &&
731 valueFormat1.sanitize_values_stride_unsafe (context, this, &values[0], count, stride) &&
732 valueFormat2.sanitize_values_stride_unsafe (context, this, &values[len1], count, stride);
736 USHORT format; /* Format identifier--format = 2 */
738 coverage; /* Offset to Coverage table--from
739 * beginning of subtable */
740 ValueFormat valueFormat1; /* ValueRecord definition--for the
741 * first glyph of the pair--may be zero
743 ValueFormat valueFormat2; /* ValueRecord definition--for the
744 * second glyph of the pair--may be
747 classDef1; /* Offset to ClassDef table--from
748 * beginning of PairPos subtable--for
749 * the first glyph of the pair */
751 classDef2; /* Offset to ClassDef table--from
752 * beginning of PairPos subtable--for
753 * the second glyph of the pair */
754 USHORT class1Count; /* Number of classes in ClassDef1
755 * table--includes Class0 */
756 USHORT class2Count; /* Number of classes in ClassDef2
757 * table--includes Class0 */
758 ValueRecord values; /* Matrix of value pairs:
759 * class1-major, class2-minor,
760 * Each entry has value1 and value2 */
762 DEFINE_SIZE_ARRAY (16, values);
767 friend struct PosLookupSubTable;
770 inline bool apply (hb_apply_context_t *context) const
774 case 1: return u.format1.apply (context);
775 case 2: return u.format2.apply (context);
776 default:return false;
780 inline bool sanitize (hb_sanitize_context_t *context) {
782 if (!u.format.sanitize (context)) return false;
784 case 1: return u.format1.sanitize (context);
785 case 2: return u.format2.sanitize (context);
792 USHORT format; /* Format identifier */
793 PairPosFormat1 format1;
794 PairPosFormat2 format2;
799 struct EntryExitRecord
801 friend struct CursivePosFormat1;
803 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
805 return entryAnchor.sanitize (context, base)
806 && exitAnchor.sanitize (context, base);
811 entryAnchor; /* Offset to EntryAnchor table--from
812 * beginning of CursivePos
813 * subtable--may be NULL */
815 exitAnchor; /* Offset to ExitAnchor table--from
816 * beginning of CursivePos
817 * subtable--may be NULL */
819 DEFINE_SIZE_STATIC (4);
822 struct CursivePosFormat1
824 friend struct CursivePos;
827 inline bool apply (hb_apply_context_t *context) const
830 /* Now comes the messiest part of the whole OpenType
831 specification. At first glance, cursive connections seem easy
832 to understand, but there are pitfalls! The reason is that
833 the specs don't mention how to compute the advance values
834 resp. glyph offsets. I was told it would be an omission, to
835 be fixed in the next OpenType version... Again many thanks to
836 Andrei Burago <andreib@microsoft.com> for clarifications.
838 Consider the following example:
851 glyph1: advance width = 12
854 glyph2: advance width = 11
857 LSB is 1 for both glyphs (so the boxes drawn above are glyph
858 bboxes). Writing direction is R2L; `0' denotes the glyph's
861 Now the surprising part: The advance width of the *left* glyph
862 (resp. of the *bottom* glyph) will be modified, no matter
863 whether the writing direction is L2R or R2L (resp. T2B or
864 B2T)! This assymetry is caused by the fact that the glyph's
865 coordinate origin is always the lower left corner for all
868 Continuing the above example, we can compute the new
869 (horizontal) advance width of glyph2 as
873 and the new vertical offset of glyph2 as
878 Vertical writing direction is far more complicated:
880 a) Assuming that we recompute the advance height of the lower glyph:
887 yadv2 | 0+--+------+ -- BSB1 --
890 BSB2 -- 0+--------+ --
893 glyph1: advance height = 6
896 glyph2: advance height = 7
899 TSB is 1 for both glyphs; writing direction is T2B.
902 BSB1 = yadv1 - (TSB1 + ymax1)
903 BSB2 = yadv2 - (TSB2 + ymax2)
906 vertical advance width of glyph2
907 = y_offset + BSB2 - BSB1
908 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
909 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
910 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
913 b) Assuming that we recompute the advance height of the upper glyph:
918 TSB2 -- +-----+--+ 1 | yadv1 ymax1
920 yadv2 | 0+--+------+ -- --
921 ymax2 | 2 | -- y_offset
926 glyph1: advance height = 6
929 glyph2: advance height = 7
932 TSB is 1 for both glyphs; writing direction is T2B.
936 vertical advance width of glyph2
937 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
938 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
941 Comparing a) with b) shows that b) is easier to compute. I'll wait
942 for a reply from Andrei to see what should really be implemented...
944 Since horizontal advance widths or vertical advance heights
945 can be used alone but not together, no ambiguity occurs. */
947 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
948 hb_codepoint_t last_pos = gpi->last;
949 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
951 /* We don't handle mark glyphs here. */
952 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
955 unsigned int index = (this+coverage) (IN_CURGLYPH ());
956 if (likely (index == NOT_COVERED))
959 const EntryExitRecord &record = entryExitRecord[index];
961 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
964 hb_position_t entry_x, entry_y;
965 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
969 if (context->buffer->direction == HB_DIRECTION_RTL)
971 /* advance is absolute, not relative */
972 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
976 /* advance is absolute, not relative */
977 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
980 if (context->lookup_flag & LookupFlag::RightToLeft)
982 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
983 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
987 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
988 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
992 if (record.exitAnchor)
994 gpi->last = context->buffer->in_pos;
995 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
998 context->buffer->in_pos++;
1002 inline bool sanitize (hb_sanitize_context_t *context) {
1004 return coverage.sanitize (context, this)
1005 && entryExitRecord.sanitize (context, this);
1009 USHORT format; /* Format identifier--format = 1 */
1011 coverage; /* Offset to Coverage table--from
1012 * beginning of subtable */
1013 ArrayOf<EntryExitRecord>
1014 entryExitRecord; /* Array of EntryExit records--in
1015 * Coverage Index order */
1017 DEFINE_SIZE_ARRAY (6, entryExitRecord);
1022 friend struct PosLookupSubTable;
1025 inline bool apply (hb_apply_context_t *context) const
1029 case 1: return u.format1.apply (context);
1030 default:return false;
1034 inline bool sanitize (hb_sanitize_context_t *context) {
1036 if (!u.format.sanitize (context)) return false;
1038 case 1: return u.format1.sanitize (context);
1039 default:return true;
1045 USHORT format; /* Format identifier */
1046 CursivePosFormat1 format1;
1051 typedef AnchorMatrix BaseArray; /* base-major--
1052 * in order of BaseCoverage Index--,
1054 * ordered by class--zero-based. */
1056 struct MarkBasePosFormat1
1058 friend struct MarkBasePos;
1061 inline bool apply (hb_apply_context_t *context) const
1064 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1065 if (likely (mark_index == NOT_COVERED))
1068 /* now we search backwards for a non-mark glyph */
1069 unsigned int property;
1070 unsigned int j = context->buffer->in_pos;
1076 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1078 /* The following assertion is too strong, so we've disabled it. */
1079 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1082 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1083 if (base_index == NOT_COVERED)
1086 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1089 inline bool sanitize (hb_sanitize_context_t *context) {
1091 return context->check_struct (this)
1092 && markCoverage.sanitize (context, this)
1093 && baseCoverage.sanitize (context, this)
1094 && markArray.sanitize (context, this)
1095 && likely (baseArray.sanitize (context, this, (unsigned int) classCount));
1099 USHORT format; /* Format identifier--format = 1 */
1101 markCoverage; /* Offset to MarkCoverage table--from
1102 * beginning of MarkBasePos subtable */
1104 baseCoverage; /* Offset to BaseCoverage table--from
1105 * beginning of MarkBasePos subtable */
1106 USHORT classCount; /* Number of classes defined for marks */
1108 markArray; /* Offset to MarkArray table--from
1109 * beginning of MarkBasePos subtable */
1111 baseArray; /* Offset to BaseArray table--from
1112 * beginning of MarkBasePos subtable */
1114 DEFINE_SIZE_STATIC (12);
1119 friend struct PosLookupSubTable;
1122 inline bool apply (hb_apply_context_t *context) const
1126 case 1: return u.format1.apply (context);
1127 default:return false;
1131 inline bool sanitize (hb_sanitize_context_t *context) {
1133 if (!u.format.sanitize (context)) return false;
1135 case 1: return u.format1.sanitize (context);
1136 default:return true;
1142 USHORT format; /* Format identifier */
1143 MarkBasePosFormat1 format1;
1148 typedef AnchorMatrix LigatureAttach; /* component-major--
1149 * in order of writing direction--,
1151 * ordered by class--zero-based. */
1153 typedef OffsetListOf<LigatureAttach> LigatureArray;
1154 /* Array of LigatureAttach
1156 * LigatureCoverage Index */
1158 struct MarkLigPosFormat1
1160 friend struct MarkLigPos;
1163 inline bool apply (hb_apply_context_t *context) const
1166 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1167 if (likely (mark_index == NOT_COVERED))
1170 /* now we search backwards for a non-mark glyph */
1171 unsigned int property;
1172 unsigned int j = context->buffer->in_pos;
1178 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1180 /* The following assertion is too strong, so we've disabled it. */
1181 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1184 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1185 if (lig_index == NOT_COVERED)
1188 const LigatureArray& lig_array = this+ligatureArray;
1189 const LigatureAttach& lig_attach = lig_array[lig_index];
1191 /* Find component to attach to */
1192 unsigned int comp_count = lig_attach.rows;
1193 if (unlikely (!comp_count))
1195 unsigned int comp_index;
1196 /* We must now check whether the ligature ID of the current mark glyph
1197 * is identical to the ligature ID of the found ligature. If yes, we
1198 * can directly use the component index. If not, we attach the mark
1199 * glyph to the last component of the ligature. */
1200 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1202 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1203 if (comp_index >= comp_count)
1204 comp_index = comp_count - 1;
1207 comp_index = comp_count - 1;
1209 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1212 inline bool sanitize (hb_sanitize_context_t *context) {
1214 return context->check_struct (this)
1215 && markCoverage.sanitize (context, this)
1216 && ligatureCoverage.sanitize (context, this)
1217 && markArray.sanitize (context, this)
1218 && likely (ligatureArray.sanitize (context, this, (unsigned int) classCount));
1222 USHORT format; /* Format identifier--format = 1 */
1224 markCoverage; /* Offset to Mark Coverage table--from
1225 * beginning of MarkLigPos subtable */
1227 ligatureCoverage; /* Offset to Ligature Coverage
1228 * table--from beginning of MarkLigPos
1230 USHORT classCount; /* Number of defined mark classes */
1232 markArray; /* Offset to MarkArray table--from
1233 * beginning of MarkLigPos subtable */
1234 OffsetTo<LigatureArray>
1235 ligatureArray; /* Offset to LigatureArray table--from
1236 * beginning of MarkLigPos subtable */
1238 DEFINE_SIZE_STATIC (12);
1243 friend struct PosLookupSubTable;
1246 inline bool apply (hb_apply_context_t *context) const
1250 case 1: return u.format1.apply (context);
1251 default:return false;
1255 inline bool sanitize (hb_sanitize_context_t *context) {
1257 if (!u.format.sanitize (context)) return false;
1259 case 1: return u.format1.sanitize (context);
1260 default:return true;
1266 USHORT format; /* Format identifier */
1267 MarkLigPosFormat1 format1;
1272 typedef AnchorMatrix Mark2Array; /* mark2-major--
1273 * in order of Mark2Coverage Index--,
1275 * ordered by class--zero-based. */
1277 struct MarkMarkPosFormat1
1279 friend struct MarkMarkPos;
1282 inline bool apply (hb_apply_context_t *context) const
1285 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1286 if (likely (mark1_index == NOT_COVERED))
1289 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1290 unsigned int property;
1291 unsigned int j = context->buffer->in_pos;
1297 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1299 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1302 /* Two marks match only if they belong to the same base, or same component
1303 * of the same ligature. That is, the component numbers must match, and
1304 * if those are non-zero, the ligid number should also match. */
1305 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1306 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1309 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1310 if (mark2_index == NOT_COVERED)
1313 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1316 inline bool sanitize (hb_sanitize_context_t *context) {
1318 return context->check_struct (this)
1319 && mark1Coverage.sanitize (context, this)
1320 && mark2Coverage.sanitize (context, this)
1321 && mark1Array.sanitize (context, this)
1322 && likely (mark2Array.sanitize (context, this, (unsigned int) classCount));
1326 USHORT format; /* Format identifier--format = 1 */
1328 mark1Coverage; /* Offset to Combining Mark1 Coverage
1329 * table--from beginning of MarkMarkPos
1332 mark2Coverage; /* Offset to Combining Mark2 Coverage
1333 * table--from beginning of MarkMarkPos
1335 USHORT classCount; /* Number of defined mark classes */
1337 mark1Array; /* Offset to Mark1Array table--from
1338 * beginning of MarkMarkPos subtable */
1339 OffsetTo<Mark2Array>
1340 mark2Array; /* Offset to Mark2Array table--from
1341 * beginning of MarkMarkPos subtable */
1343 DEFINE_SIZE_STATIC (12);
1348 friend struct PosLookupSubTable;
1351 inline bool apply (hb_apply_context_t *context) const
1355 case 1: return u.format1.apply (context);
1356 default:return false;
1360 inline bool sanitize (hb_sanitize_context_t *context) {
1362 if (!u.format.sanitize (context)) return false;
1364 case 1: return u.format1.sanitize (context);
1365 default:return true;
1371 USHORT format; /* Format identifier */
1372 MarkMarkPosFormat1 format1;
1377 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1379 struct ContextPos : Context
1381 friend struct PosLookupSubTable;
1384 inline bool apply (hb_apply_context_t *context) const
1387 return Context::apply (context, position_lookup);
1391 struct ChainContextPos : ChainContext
1393 friend struct PosLookupSubTable;
1396 inline bool apply (hb_apply_context_t *context) const
1399 return ChainContext::apply (context, position_lookup);
1404 struct ExtensionPos : Extension
1406 friend struct PosLookupSubTable;
1409 inline const struct PosLookupSubTable& get_subtable (void) const
1411 unsigned int offset = get_offset ();
1412 if (unlikely (!offset)) return Null(PosLookupSubTable);
1413 return StructAtOffset<PosLookupSubTable> (this, offset);
1416 inline bool apply (hb_apply_context_t *context) const;
1418 inline bool sanitize (hb_sanitize_context_t *context);
1428 struct PosLookupSubTable
1430 friend struct PosLookup;
1444 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1447 switch (lookup_type) {
1448 case Single: return u.single.apply (context);
1449 case Pair: return u.pair.apply (context);
1450 case Cursive: return u.cursive.apply (context);
1451 case MarkBase: return u.markBase.apply (context);
1452 case MarkLig: return u.markLig.apply (context);
1453 case MarkMark: return u.markMark.apply (context);
1454 case Context: return u.context.apply (context);
1455 case ChainContext: return u.chainContext.apply (context);
1456 case Extension: return u.extension.apply (context);
1457 default:return false;
1461 inline bool sanitize (hb_sanitize_context_t *context, unsigned int lookup_type) {
1463 if (!u.sub_format.sanitize (context)) return false;
1464 switch (lookup_type) {
1465 case Single: return u.single.sanitize (context);
1466 case Pair: return u.pair.sanitize (context);
1467 case Cursive: return u.cursive.sanitize (context);
1468 case MarkBase: return u.markBase.sanitize (context);
1469 case MarkLig: return u.markLig.sanitize (context);
1470 case MarkMark: return u.markMark.sanitize (context);
1471 case Context: return u.context.sanitize (context);
1472 case ChainContext: return u.chainContext.sanitize (context);
1473 case Extension: return u.extension.sanitize (context);
1474 default:return true;
1484 MarkBasePos markBase;
1486 MarkMarkPos markMark;
1488 ChainContextPos chainContext;
1489 ExtensionPos extension;
1492 DEFINE_SIZE_UNION (2, sub_format);
1496 struct PosLookup : Lookup
1498 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1499 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1501 inline bool apply_once (hb_ot_layout_context_t *layout,
1502 hb_buffer_t *buffer,
1503 unsigned int context_length,
1504 unsigned int nesting_level_left) const
1506 unsigned int lookup_type = get_type ();
1507 hb_apply_context_t context[1] = {{0}};
1509 context->layout = layout;
1510 context->buffer = buffer;
1511 context->context_length = context_length;
1512 context->nesting_level_left = nesting_level_left;
1513 context->lookup_flag = get_flag ();
1515 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1518 for (unsigned int i = 0; i < get_subtable_count (); i++)
1519 if (get_subtable (i).apply (context, lookup_type))
1525 inline bool apply_string (hb_ot_layout_context_t *layout,
1526 hb_buffer_t *buffer,
1527 hb_mask_t mask) const
1530 #define BUFFER buffer
1533 if (unlikely (!buffer->in_length))
1536 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1539 while (buffer->in_pos < buffer->in_length)
1542 if (~IN_MASK (buffer->in_pos) & mask)
1544 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1550 /* Contrary to properties defined in GDEF, user-defined properties
1551 will always stop a possible cursive positioning. */
1552 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1562 inline bool sanitize (hb_sanitize_context_t *context) {
1564 if (unlikely (!Lookup::sanitize (context))) return false;
1565 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1566 return list.sanitize (context, this, get_type ());
1570 typedef OffsetListOf<PosLookup> PosLookupList;
1576 struct GPOS : GSUBGPOS
1578 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1580 inline const PosLookup& get_lookup (unsigned int i) const
1581 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1583 inline bool position_lookup (hb_ot_layout_context_t *layout,
1584 hb_buffer_t *buffer,
1585 unsigned int lookup_index,
1586 hb_mask_t mask) const
1587 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1589 inline bool sanitize (hb_sanitize_context_t *context) {
1591 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1592 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1593 return list.sanitize (context, this);
1596 DEFINE_SIZE_STATIC (10);
1600 /* Out-of-class implementation for methods recursing */
1602 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1605 return get_subtable ().apply (context, get_type ());
1608 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1611 if (unlikely (!Extension::sanitize (context))) return false;
1612 unsigned int offset = get_offset ();
1613 if (unlikely (!offset)) return true;
1614 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (context, get_type ());
1617 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1619 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1620 const PosLookup &l = gpos.get_lookup (lookup_index);
1622 if (unlikely (context->nesting_level_left == 0))
1625 if (unlikely (context->context_length < 1))
1628 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1632 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */