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[VAR0];
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[VAR];
335 AnchorFormat2 format2[VAR];
336 AnchorFormat3 format3[VAR];
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_VAR (2, OffsetTo<Anchor>);
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_VAR (6, Value);
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_VAR (8, Value);
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[VAR];
541 SinglePosFormat2 format2[VAR];
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_VAR (2, Value);
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 */
575 array[VAR]; /* Array of PairValueRecords--ordered
576 * by GlyphID of the second glyph */
578 DEFINE_SIZE_VAR (2, PairValueRecord);
581 struct PairPosFormat1
583 friend struct PairPos;
586 inline bool apply (hb_apply_context_t *context) const
589 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
590 if (unlikely (context->buffer->in_pos + 2 > end))
593 unsigned int index = (this+coverage) (IN_CURGLYPH ());
594 if (likely (index == NOT_COVERED))
597 unsigned int j = context->buffer->in_pos + 1;
598 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
600 if (unlikely (j == end))
605 unsigned int len1 = valueFormat1.get_len ();
606 unsigned int len2 = valueFormat2.get_len ();
607 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
609 const PairSet &pair_set = this+pairSet[index];
610 unsigned int count = pair_set.len;
611 const PairValueRecord *record = pair_set.array;
612 for (unsigned int i = 0; i < count; i++)
614 if (IN_GLYPH (j) == record->secondGlyph)
616 valueFormat1.apply_value (context->layout, this, &record->values[0], CURPOSITION ());
617 valueFormat2.apply_value (context->layout, this, &record->values[len1], POSITION (j));
620 context->buffer->in_pos = j;
623 record = &StructAtOffset<PairValueRecord> (record, record_size);
629 inline bool sanitize (hb_sanitize_context_t *context) {
632 unsigned int len1 = valueFormat1.get_len ();
633 unsigned int len2 = valueFormat2.get_len ();
635 if (!(context->check_struct (this)
636 && coverage.sanitize (context, this)
637 && likely (pairSet.sanitize (context, this, len1 + len2)))) return false;
639 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
641 unsigned int stride = 1 + len1 + len2;
642 unsigned int count1 = pairSet.len;
643 for (unsigned int i = 0; i < count1; i++)
645 PairSet &pair_set = const_cast<PairSet &> (this+pairSet[i]); /* XXX clean this up */
647 unsigned int count2 = pair_set.len;
648 PairValueRecord *record = pair_set.array;
649 if (!(valueFormat1.sanitize_values_stride_unsafe (context, this, &record->values[0], count2, stride) &&
650 valueFormat2.sanitize_values_stride_unsafe (context, this, &record->values[len1], count2, stride)))
658 USHORT format; /* Format identifier--format = 1 */
660 coverage; /* Offset to Coverage table--from
661 * beginning of subtable */
662 ValueFormat valueFormat1; /* Defines the types of data in
663 * ValueRecord1--for the first glyph
664 * in the pair--may be zero (0) */
665 ValueFormat valueFormat2; /* Defines the types of data in
666 * ValueRecord2--for the second glyph
667 * in the pair--may be zero (0) */
668 OffsetArrayOf<PairSet>
669 pairSet; /* Array of PairSet tables
670 * ordered by Coverage Index */
672 DEFINE_SIZE_VAR (10, OffsetTo<PairSet>);
675 struct PairPosFormat2
677 friend struct PairPos;
680 inline bool apply (hb_apply_context_t *context) const
683 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
684 if (unlikely (context->buffer->in_pos + 2 > end))
687 unsigned int index = (this+coverage) (IN_CURGLYPH ());
688 if (likely (index == NOT_COVERED))
691 unsigned int j = context->buffer->in_pos + 1;
692 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
694 if (unlikely (j == end))
699 unsigned int len1 = valueFormat1.get_len ();
700 unsigned int len2 = valueFormat2.get_len ();
701 unsigned int record_len = len1 + len2;
703 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
704 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
705 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
708 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
709 valueFormat1.apply_value (context->layout, this, v, CURPOSITION ());
710 valueFormat2.apply_value (context->layout, this, v + len1, POSITION (j));
714 context->buffer->in_pos = j;
719 inline bool sanitize (hb_sanitize_context_t *context) {
721 if (!(context->check_struct (this)
722 && coverage.sanitize (context, this)
723 && classDef1.sanitize (context, this)
724 && classDef2.sanitize (context, this))) return false;
726 unsigned int len1 = valueFormat1.get_len ();
727 unsigned int len2 = valueFormat2.get_len ();
728 unsigned int stride = len1 + len2;
729 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
730 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
731 return context->check_array (values, record_size, count) &&
732 valueFormat1.sanitize_values_stride_unsafe (context, this, &values[0], count, stride) &&
733 valueFormat2.sanitize_values_stride_unsafe (context, this, &values[len1], count, stride);
737 USHORT format; /* Format identifier--format = 2 */
739 coverage; /* Offset to Coverage table--from
740 * beginning of subtable */
741 ValueFormat valueFormat1; /* ValueRecord definition--for the
742 * first glyph of the pair--may be zero
744 ValueFormat valueFormat2; /* ValueRecord definition--for the
745 * second glyph of the pair--may be
748 classDef1; /* Offset to ClassDef table--from
749 * beginning of PairPos subtable--for
750 * the first glyph of the pair */
752 classDef2; /* Offset to ClassDef table--from
753 * beginning of PairPos subtable--for
754 * the second glyph of the pair */
755 USHORT class1Count; /* Number of classes in ClassDef1
756 * table--includes Class0 */
757 USHORT class2Count; /* Number of classes in ClassDef2
758 * table--includes Class0 */
759 ValueRecord values; /* Matrix of value pairs:
760 * class1-major, class2-minor,
761 * Each entry has value1 and value2 */
763 DEFINE_SIZE_VAR (16, ValueRecord);
768 friend struct PosLookupSubTable;
771 inline bool apply (hb_apply_context_t *context) const
775 case 1: return u.format1->apply (context);
776 case 2: return u.format2->apply (context);
777 default:return false;
781 inline bool sanitize (hb_sanitize_context_t *context) {
783 if (!u.format.sanitize (context)) return false;
785 case 1: return u.format1->sanitize (context);
786 case 2: return u.format2->sanitize (context);
793 USHORT format; /* Format identifier */
794 PairPosFormat1 format1[VAR];
795 PairPosFormat2 format2[VAR];
800 struct EntryExitRecord
802 friend struct CursivePosFormat1;
804 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
806 return entryAnchor.sanitize (context, base)
807 && exitAnchor.sanitize (context, base);
812 entryAnchor; /* Offset to EntryAnchor table--from
813 * beginning of CursivePos
814 * subtable--may be NULL */
816 exitAnchor; /* Offset to ExitAnchor table--from
817 * beginning of CursivePos
818 * subtable--may be NULL */
820 DEFINE_SIZE_STATIC (4);
823 struct CursivePosFormat1
825 friend struct CursivePos;
828 inline bool apply (hb_apply_context_t *context) const
831 /* Now comes the messiest part of the whole OpenType
832 specification. At first glance, cursive connections seem easy
833 to understand, but there are pitfalls! The reason is that
834 the specs don't mention how to compute the advance values
835 resp. glyph offsets. I was told it would be an omission, to
836 be fixed in the next OpenType version... Again many thanks to
837 Andrei Burago <andreib@microsoft.com> for clarifications.
839 Consider the following example:
852 glyph1: advance width = 12
855 glyph2: advance width = 11
858 LSB is 1 for both glyphs (so the boxes drawn above are glyph
859 bboxes). Writing direction is R2L; `0' denotes the glyph's
862 Now the surprising part: The advance width of the *left* glyph
863 (resp. of the *bottom* glyph) will be modified, no matter
864 whether the writing direction is L2R or R2L (resp. T2B or
865 B2T)! This assymetry is caused by the fact that the glyph's
866 coordinate origin is always the lower left corner for all
869 Continuing the above example, we can compute the new
870 (horizontal) advance width of glyph2 as
874 and the new vertical offset of glyph2 as
879 Vertical writing direction is far more complicated:
881 a) Assuming that we recompute the advance height of the lower glyph:
888 yadv2 | 0+--+------+ -- BSB1 --
891 BSB2 -- 0+--------+ --
894 glyph1: advance height = 6
897 glyph2: advance height = 7
900 TSB is 1 for both glyphs; writing direction is T2B.
903 BSB1 = yadv1 - (TSB1 + ymax1)
904 BSB2 = yadv2 - (TSB2 + ymax2)
907 vertical advance width of glyph2
908 = y_offset + BSB2 - BSB1
909 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
910 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
911 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
914 b) Assuming that we recompute the advance height of the upper glyph:
919 TSB2 -- +-----+--+ 1 | yadv1 ymax1
921 yadv2 | 0+--+------+ -- --
922 ymax2 | 2 | -- y_offset
927 glyph1: advance height = 6
930 glyph2: advance height = 7
933 TSB is 1 for both glyphs; writing direction is T2B.
937 vertical advance width of glyph2
938 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
939 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
942 Comparing a) with b) shows that b) is easier to compute. I'll wait
943 for a reply from Andrei to see what should really be implemented...
945 Since horizontal advance widths or vertical advance heights
946 can be used alone but not together, no ambiguity occurs. */
948 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
949 hb_codepoint_t last_pos = gpi->last;
950 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
952 /* We don't handle mark glyphs here. */
953 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
956 unsigned int index = (this+coverage) (IN_CURGLYPH ());
957 if (likely (index == NOT_COVERED))
960 const EntryExitRecord &record = entryExitRecord[index];
962 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
965 hb_position_t entry_x, entry_y;
966 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
970 if (context->buffer->direction == HB_DIRECTION_RTL)
972 /* advance is absolute, not relative */
973 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
977 /* advance is absolute, not relative */
978 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
981 if (context->lookup_flag & LookupFlag::RightToLeft)
983 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
984 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
988 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
989 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
993 if (record.exitAnchor)
995 gpi->last = context->buffer->in_pos;
996 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
999 context->buffer->in_pos++;
1003 inline bool sanitize (hb_sanitize_context_t *context) {
1005 return coverage.sanitize (context, this)
1006 && entryExitRecord.sanitize (context, this);
1010 USHORT format; /* Format identifier--format = 1 */
1012 coverage; /* Offset to Coverage table--from
1013 * beginning of subtable */
1014 ArrayOf<EntryExitRecord>
1015 entryExitRecord; /* Array of EntryExit records--in
1016 * Coverage Index order */
1018 DEFINE_SIZE_VAR (6, EntryExitRecord);
1023 friend struct PosLookupSubTable;
1026 inline bool apply (hb_apply_context_t *context) const
1030 case 1: return u.format1->apply (context);
1031 default:return false;
1035 inline bool sanitize (hb_sanitize_context_t *context) {
1037 if (!u.format.sanitize (context)) return false;
1039 case 1: return u.format1->sanitize (context);
1040 default:return true;
1046 USHORT format; /* Format identifier */
1047 CursivePosFormat1 format1[VAR];
1052 typedef AnchorMatrix BaseArray; /* base-major--
1053 * in order of BaseCoverage Index--,
1055 * ordered by class--zero-based. */
1057 struct MarkBasePosFormat1
1059 friend struct MarkBasePos;
1062 inline bool apply (hb_apply_context_t *context) const
1065 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1066 if (likely (mark_index == NOT_COVERED))
1069 /* now we search backwards for a non-mark glyph */
1070 unsigned int property;
1071 unsigned int j = context->buffer->in_pos;
1077 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1079 /* The following assertion is too strong, so we've disabled it. */
1080 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1083 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1084 if (base_index == NOT_COVERED)
1087 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1090 inline bool sanitize (hb_sanitize_context_t *context) {
1092 return context->check_struct (this)
1093 && markCoverage.sanitize (context, this)
1094 && baseCoverage.sanitize (context, this)
1095 && markArray.sanitize (context, this)
1096 && likely (baseArray.sanitize (context, this, (unsigned int) classCount));
1100 USHORT format; /* Format identifier--format = 1 */
1102 markCoverage; /* Offset to MarkCoverage table--from
1103 * beginning of MarkBasePos subtable */
1105 baseCoverage; /* Offset to BaseCoverage table--from
1106 * beginning of MarkBasePos subtable */
1107 USHORT classCount; /* Number of classes defined for marks */
1109 markArray; /* Offset to MarkArray table--from
1110 * beginning of MarkBasePos subtable */
1112 baseArray; /* Offset to BaseArray table--from
1113 * beginning of MarkBasePos subtable */
1115 DEFINE_SIZE_STATIC (12);
1120 friend struct PosLookupSubTable;
1123 inline bool apply (hb_apply_context_t *context) const
1127 case 1: return u.format1->apply (context);
1128 default:return false;
1132 inline bool sanitize (hb_sanitize_context_t *context) {
1134 if (!u.format.sanitize (context)) return false;
1136 case 1: return u.format1->sanitize (context);
1137 default:return true;
1143 USHORT format; /* Format identifier */
1144 MarkBasePosFormat1 format1[VAR];
1149 typedef AnchorMatrix LigatureAttach; /* component-major--
1150 * in order of writing direction--,
1152 * ordered by class--zero-based. */
1154 typedef OffsetListOf<LigatureAttach> LigatureArray;
1155 /* Array of LigatureAttach
1157 * LigatureCoverage Index */
1159 struct MarkLigPosFormat1
1161 friend struct MarkLigPos;
1164 inline bool apply (hb_apply_context_t *context) const
1167 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1168 if (likely (mark_index == NOT_COVERED))
1171 /* now we search backwards for a non-mark glyph */
1172 unsigned int property;
1173 unsigned int j = context->buffer->in_pos;
1179 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1181 /* The following assertion is too strong, so we've disabled it. */
1182 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1185 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1186 if (lig_index == NOT_COVERED)
1189 const LigatureArray& lig_array = this+ligatureArray;
1190 const LigatureAttach& lig_attach = lig_array[lig_index];
1192 /* Find component to attach to */
1193 unsigned int comp_count = lig_attach.rows;
1194 if (unlikely (!comp_count))
1196 unsigned int comp_index;
1197 /* We must now check whether the ligature ID of the current mark glyph
1198 * is identical to the ligature ID of the found ligature. If yes, we
1199 * can directly use the component index. If not, we attach the mark
1200 * glyph to the last component of the ligature. */
1201 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1203 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1204 if (comp_index >= comp_count)
1205 comp_index = comp_count - 1;
1208 comp_index = comp_count - 1;
1210 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1213 inline bool sanitize (hb_sanitize_context_t *context) {
1215 return context->check_struct (this)
1216 && markCoverage.sanitize (context, this)
1217 && ligatureCoverage.sanitize (context, this)
1218 && markArray.sanitize (context, this)
1219 && likely (ligatureArray.sanitize (context, this, (unsigned int) classCount));
1223 USHORT format; /* Format identifier--format = 1 */
1225 markCoverage; /* Offset to Mark Coverage table--from
1226 * beginning of MarkLigPos subtable */
1228 ligatureCoverage; /* Offset to Ligature Coverage
1229 * table--from beginning of MarkLigPos
1231 USHORT classCount; /* Number of defined mark classes */
1233 markArray; /* Offset to MarkArray table--from
1234 * beginning of MarkLigPos subtable */
1235 OffsetTo<LigatureArray>
1236 ligatureArray; /* Offset to LigatureArray table--from
1237 * beginning of MarkLigPos subtable */
1239 DEFINE_SIZE_STATIC (12);
1244 friend struct PosLookupSubTable;
1247 inline bool apply (hb_apply_context_t *context) const
1251 case 1: return u.format1->apply (context);
1252 default:return false;
1256 inline bool sanitize (hb_sanitize_context_t *context) {
1258 if (!u.format.sanitize (context)) return false;
1260 case 1: return u.format1->sanitize (context);
1261 default:return true;
1267 USHORT format; /* Format identifier */
1268 MarkLigPosFormat1 format1[VAR];
1273 typedef AnchorMatrix Mark2Array; /* mark2-major--
1274 * in order of Mark2Coverage Index--,
1276 * ordered by class--zero-based. */
1278 struct MarkMarkPosFormat1
1280 friend struct MarkMarkPos;
1283 inline bool apply (hb_apply_context_t *context) const
1286 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1287 if (likely (mark1_index == NOT_COVERED))
1290 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1291 unsigned int property;
1292 unsigned int j = context->buffer->in_pos;
1298 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1300 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1303 /* Two marks match only if they belong to the same base, or same component
1304 * of the same ligature. That is, the component numbers must match, and
1305 * if those are non-zero, the ligid number should also match. */
1306 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1307 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1310 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1311 if (mark2_index == NOT_COVERED)
1314 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1317 inline bool sanitize (hb_sanitize_context_t *context) {
1319 return context->check_struct (this)
1320 && mark1Coverage.sanitize (context, this)
1321 && mark2Coverage.sanitize (context, this)
1322 && mark1Array.sanitize (context, this)
1323 && likely (mark2Array.sanitize (context, this, (unsigned int) classCount));
1327 USHORT format; /* Format identifier--format = 1 */
1329 mark1Coverage; /* Offset to Combining Mark1 Coverage
1330 * table--from beginning of MarkMarkPos
1333 mark2Coverage; /* Offset to Combining Mark2 Coverage
1334 * table--from beginning of MarkMarkPos
1336 USHORT classCount; /* Number of defined mark classes */
1338 mark1Array; /* Offset to Mark1Array table--from
1339 * beginning of MarkMarkPos subtable */
1340 OffsetTo<Mark2Array>
1341 mark2Array; /* Offset to Mark2Array table--from
1342 * beginning of MarkMarkPos subtable */
1344 DEFINE_SIZE_STATIC (12);
1349 friend struct PosLookupSubTable;
1352 inline bool apply (hb_apply_context_t *context) const
1356 case 1: return u.format1->apply (context);
1357 default:return false;
1361 inline bool sanitize (hb_sanitize_context_t *context) {
1363 if (!u.format.sanitize (context)) return false;
1365 case 1: return u.format1->sanitize (context);
1366 default:return true;
1372 USHORT format; /* Format identifier */
1373 MarkMarkPosFormat1 format1[VAR];
1378 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1380 struct ContextPos : Context
1382 friend struct PosLookupSubTable;
1385 inline bool apply (hb_apply_context_t *context) const
1388 return Context::apply (context, position_lookup);
1392 struct ChainContextPos : ChainContext
1394 friend struct PosLookupSubTable;
1397 inline bool apply (hb_apply_context_t *context) const
1400 return ChainContext::apply (context, position_lookup);
1405 struct ExtensionPos : Extension
1407 friend struct PosLookupSubTable;
1410 inline const struct PosLookupSubTable& get_subtable (void) const
1412 unsigned int offset = get_offset ();
1413 if (unlikely (!offset)) return Null(PosLookupSubTable);
1414 return StructAtOffset<PosLookupSubTable> (this, offset);
1417 inline bool apply (hb_apply_context_t *context) const;
1419 inline bool sanitize (hb_sanitize_context_t *context);
1429 struct PosLookupSubTable
1431 friend struct PosLookup;
1445 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1448 switch (lookup_type) {
1449 case Single: return u.single->apply (context);
1450 case Pair: return u.pair->apply (context);
1451 case Cursive: return u.cursive->apply (context);
1452 case MarkBase: return u.markBase->apply (context);
1453 case MarkLig: return u.markLig->apply (context);
1454 case MarkMark: return u.markMark->apply (context);
1455 case Context: return u.context->apply (context);
1456 case ChainContext: return u.chainContext->apply (context);
1457 case Extension: return u.extension->apply (context);
1458 default:return false;
1462 inline bool sanitize (hb_sanitize_context_t *context) {
1464 if (!u.format.sanitize (context)) return false;
1466 case Single: return u.single->sanitize (context);
1467 case Pair: return u.pair->sanitize (context);
1468 case Cursive: return u.cursive->sanitize (context);
1469 case MarkBase: return u.markBase->sanitize (context);
1470 case MarkLig: return u.markLig->sanitize (context);
1471 case MarkMark: return u.markMark->sanitize (context);
1472 case Context: return u.context->sanitize (context);
1473 case ChainContext: return u.chainContext->sanitize (context);
1474 case Extension: return u.extension->sanitize (context);
1475 default:return true;
1482 SinglePos single[VAR];
1484 CursivePos cursive[VAR];
1485 MarkBasePos markBase[VAR];
1486 MarkLigPos markLig[VAR];
1487 MarkMarkPos markMark[VAR];
1488 ContextPos context[VAR];
1489 ChainContextPos chainContext[VAR];
1490 ExtensionPos extension[VAR];
1493 DEFINE_SIZE_MIN (2);
1497 struct PosLookup : Lookup
1499 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1500 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1502 inline bool apply_once (hb_ot_layout_context_t *layout,
1503 hb_buffer_t *buffer,
1504 unsigned int context_length,
1505 unsigned int nesting_level_left) const
1507 unsigned int lookup_type = get_type ();
1508 hb_apply_context_t context[1] = {{0}};
1510 context->layout = layout;
1511 context->buffer = buffer;
1512 context->context_length = context_length;
1513 context->nesting_level_left = nesting_level_left;
1514 context->lookup_flag = get_flag ();
1516 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1519 for (unsigned int i = 0; i < get_subtable_count (); i++)
1520 if (get_subtable (i).apply (context, lookup_type))
1526 inline bool apply_string (hb_ot_layout_context_t *layout,
1527 hb_buffer_t *buffer,
1528 hb_mask_t mask) const
1531 #define BUFFER buffer
1534 if (unlikely (!buffer->in_length))
1537 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1540 while (buffer->in_pos < buffer->in_length)
1543 if (~IN_MASK (buffer->in_pos) & mask)
1545 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1551 /* Contrary to properties defined in GDEF, user-defined properties
1552 will always stop a possible cursive positioning. */
1553 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1563 inline bool sanitize (hb_sanitize_context_t *context) {
1565 if (unlikely (!Lookup::sanitize (context))) return false;
1566 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1567 return list.sanitize (context, this);
1571 typedef OffsetListOf<PosLookup> PosLookupList;
1577 struct GPOS : GSUBGPOS
1579 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1581 inline const PosLookup& get_lookup (unsigned int i) const
1582 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1584 inline bool position_lookup (hb_ot_layout_context_t *layout,
1585 hb_buffer_t *buffer,
1586 unsigned int lookup_index,
1587 hb_mask_t mask) const
1588 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1590 inline bool sanitize (hb_sanitize_context_t *context) {
1592 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1593 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1594 return list.sanitize (context, this);
1597 DEFINE_SIZE_STATIC (10);
1601 /* Out-of-class implementation for methods recursing */
1603 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1606 return get_subtable ().apply (context, get_type ());
1609 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1612 if (unlikely (!Extension::sanitize (context))) return false;
1613 unsigned int offset = get_offset ();
1614 if (unlikely (!offset)) return true;
1615 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (context);
1618 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1620 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1621 const PosLookup &l = gpos.get_lookup (lookup_index);
1623 if (unlikely (context->nesting_level_left == 0))
1626 if (unlikely (context->context_length < 1))
1629 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1633 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */