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];
343 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
344 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
345 return this+matrix[row * cols + col];
348 inline bool sanitize (hb_sanitize_context_t *context, unsigned int cols) {
350 if (!context->check_struct (this)) return false;
351 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
352 unsigned int count = rows * cols;
353 if (!context->check_array (matrix, matrix[0].static_size, count)) return false;
354 for (unsigned int i = 0; i < count; i++)
355 if (!matrix[i].sanitize (context, this)) return false;
359 USHORT rows; /* Number of rows */
362 matrix[VAR]; /* Matrix of offsets to Anchor tables--
363 * from beginning of AnchorMatrix table */
365 DEFINE_SIZE_VAR (2, OffsetTo<Anchor>);
371 friend struct MarkArray;
373 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
375 return context->check_struct (this)
376 && markAnchor.sanitize (context, base);
380 USHORT klass; /* Class defined for this mark */
382 markAnchor; /* Offset to Anchor table--from
383 * beginning of MarkArray table */
385 DEFINE_SIZE_STATIC (4);
390 inline bool apply (hb_apply_context_t *context,
391 unsigned int mark_index, unsigned int glyph_index,
392 const AnchorMatrix &anchors, unsigned int class_count,
393 unsigned int glyph_pos) const
396 const MarkRecord &record = markRecord[mark_index];
397 unsigned int mark_class = record.klass;
399 const Anchor& mark_anchor = this + record.markAnchor;
400 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
402 hb_position_t mark_x, mark_y, base_x, base_y;
404 mark_anchor.get_anchor (context->layout, IN_CURGLYPH (), &mark_x, &mark_y);
405 glyph_anchor.get_anchor (context->layout, IN_GLYPH (glyph_pos), &base_x, &base_y);
407 hb_internal_glyph_position_t *o = POSITION (context->buffer->in_pos);
410 o->x_offset = base_x - mark_x;
411 o->y_offset = base_y - mark_y;
412 o->back = context->buffer->in_pos - glyph_pos;
414 context->buffer->in_pos++;
418 inline bool sanitize (hb_sanitize_context_t *context) {
420 return markRecord.sanitize (context, this);
425 markRecord; /* Array of MarkRecords--in Coverage order */
427 DEFINE_SIZE_STATIC (2);
433 struct SinglePosFormat1
435 friend struct SinglePos;
438 inline bool apply (hb_apply_context_t *context) const
441 unsigned int index = (this+coverage) (IN_CURGLYPH ());
442 if (likely (index == NOT_COVERED))
445 valueFormat.apply_value (context->layout, CharP(this), values, CURPOSITION ());
447 context->buffer->in_pos++;
451 inline bool sanitize (hb_sanitize_context_t *context) {
453 return context->check_struct (this)
454 && coverage.sanitize (context, this)
455 && valueFormat.sanitize_value (context, CharP(this), values);
459 USHORT format; /* Format identifier--format = 1 */
461 coverage; /* Offset to Coverage table--from
462 * beginning of subtable */
463 ValueFormat valueFormat; /* Defines the types of data in the
465 ValueRecord values; /* Defines positioning
466 * value(s)--applied to all glyphs in
467 * the Coverage table */
469 DEFINE_SIZE_VAR (6, ValueRecord);
472 struct SinglePosFormat2
474 friend struct SinglePos;
477 inline bool apply (hb_apply_context_t *context) const
480 unsigned int index = (this+coverage) (IN_CURGLYPH ());
481 if (likely (index == NOT_COVERED))
484 if (likely (index >= valueCount))
487 valueFormat.apply_value (context->layout, CharP(this),
488 &values[index * valueFormat.get_len ()],
491 context->buffer->in_pos++;
495 inline bool sanitize (hb_sanitize_context_t *context) {
497 return context->check_struct (this)
498 && coverage.sanitize (context, this)
499 && valueFormat.sanitize_values (context, CharP(this), values, valueCount);
503 USHORT format; /* Format identifier--format = 2 */
505 coverage; /* Offset to Coverage table--from
506 * beginning of subtable */
507 ValueFormat valueFormat; /* Defines the types of data in the
509 USHORT valueCount; /* Number of ValueRecords */
510 ValueRecord values; /* Array of ValueRecords--positioning
511 * values applied to glyphs */
513 DEFINE_SIZE_VAR (8, ValueRecord);
518 friend struct PosLookupSubTable;
521 inline bool apply (hb_apply_context_t *context) const
525 case 1: return u.format1->apply (context);
526 case 2: return u.format2->apply (context);
527 default:return false;
531 inline bool sanitize (hb_sanitize_context_t *context) {
533 if (!u.format.sanitize (context)) return false;
535 case 1: return u.format1->sanitize (context);
536 case 2: return u.format2->sanitize (context);
543 USHORT format; /* Format identifier */
544 SinglePosFormat1 format1[VAR];
545 SinglePosFormat2 format2[VAR];
550 struct PairValueRecord
552 friend struct PairPosFormat1;
555 GlyphID secondGlyph; /* GlyphID of second glyph in the
556 * pair--first glyph is listed in the
558 ValueRecord values; /* Positioning data for the first glyph
559 * followed by for second glyph */
561 DEFINE_SIZE_VAR (2, ValueRecord);
566 friend struct PairPosFormat1;
568 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
569 inline bool sanitize (hb_sanitize_context_t *context, unsigned int format_len) {
571 if (!context->check_struct (this)) return false;
572 unsigned int count = (1 + format_len) * len;
573 return context->check_array (array, USHORT::static_size, count);
577 USHORT len; /* Number of PairValueRecords */
579 array[VAR]; /* Array of PairValueRecords--ordered
580 * by GlyphID of the second glyph */
582 DEFINE_SIZE_VAR (2, PairValueRecord);
585 struct PairPosFormat1
587 friend struct PairPos;
590 inline bool apply (hb_apply_context_t *context) const
593 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
594 if (unlikely (context->buffer->in_pos + 2 > end))
597 unsigned int index = (this+coverage) (IN_CURGLYPH ());
598 if (likely (index == NOT_COVERED))
601 unsigned int j = context->buffer->in_pos + 1;
602 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
604 if (unlikely (j == end))
609 unsigned int len1 = valueFormat1.get_len ();
610 unsigned int len2 = valueFormat2.get_len ();
611 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
613 const PairSet &pair_set = this+pairSet[index];
614 unsigned int count = pair_set.len;
615 const PairValueRecord *record = pair_set.array;
616 for (unsigned int i = 0; i < count; i++)
618 if (IN_GLYPH (j) == record->secondGlyph)
620 valueFormat1.apply_value (context->layout, CharP(this), &record->values[0], CURPOSITION ());
621 valueFormat2.apply_value (context->layout, CharP(this), &record->values[len1], POSITION (j));
624 context->buffer->in_pos = j;
627 record = &StructAtOffset<PairValueRecord> (*record, record_size);
633 inline bool sanitize (hb_sanitize_context_t *context) {
636 unsigned int len1 = valueFormat1.get_len ();
637 unsigned int len2 = valueFormat2.get_len ();
639 if (!(context->check_struct (this)
640 && coverage.sanitize (context, this)
641 && likely (pairSet.sanitize (context, CharP(this), len1 + len2)))) return false;
643 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
645 unsigned int stride = 1 + len1 + len2;
646 unsigned int count1 = pairSet.len;
647 for (unsigned int i = 0; i < count1; i++)
649 PairSet &pair_set = const_cast<PairSet &> (this+pairSet[i]); /* XXX clean this up */
651 unsigned int count2 = pair_set.len;
652 PairValueRecord *record = pair_set.array;
653 if (!(valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &record->values[0], count2, stride) &&
654 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &record->values[len1], count2, stride)))
662 USHORT format; /* Format identifier--format = 1 */
664 coverage; /* Offset to Coverage table--from
665 * beginning of subtable */
666 ValueFormat valueFormat1; /* Defines the types of data in
667 * ValueRecord1--for the first glyph
668 * in the pair--may be zero (0) */
669 ValueFormat valueFormat2; /* Defines the types of data in
670 * ValueRecord2--for the second glyph
671 * in the pair--may be zero (0) */
672 OffsetArrayOf<PairSet>
673 pairSet; /* Array of PairSet tables
674 * ordered by Coverage Index */
676 DEFINE_SIZE_STATIC (10);
679 struct PairPosFormat2
681 friend struct PairPos;
684 inline bool apply (hb_apply_context_t *context) const
687 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
688 if (unlikely (context->buffer->in_pos + 2 > end))
691 unsigned int index = (this+coverage) (IN_CURGLYPH ());
692 if (likely (index == NOT_COVERED))
695 unsigned int j = context->buffer->in_pos + 1;
696 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
698 if (unlikely (j == end))
703 unsigned int len1 = valueFormat1.get_len ();
704 unsigned int len2 = valueFormat2.get_len ();
705 unsigned int record_len = len1 + len2;
707 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
708 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
709 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
712 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
713 valueFormat1.apply_value (context->layout, CharP(this), v, CURPOSITION ());
714 valueFormat2.apply_value (context->layout, CharP(this), v + len1, POSITION (j));
718 context->buffer->in_pos = j;
723 inline bool sanitize (hb_sanitize_context_t *context) {
725 if (!(context->check_struct (this)
726 && coverage.sanitize (context, this)
727 && classDef1.sanitize (context, this)
728 && classDef2.sanitize (context, this))) return false;
730 unsigned int len1 = valueFormat1.get_len ();
731 unsigned int len2 = valueFormat2.get_len ();
732 unsigned int stride = len1 + len2;
733 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
734 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
735 return context->check_array (values, record_size, count) &&
736 valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &values[0], count, stride) &&
737 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &values[len1], count, stride);
741 USHORT format; /* Format identifier--format = 2 */
743 coverage; /* Offset to Coverage table--from
744 * beginning of subtable */
745 ValueFormat valueFormat1; /* ValueRecord definition--for the
746 * first glyph of the pair--may be zero
748 ValueFormat valueFormat2; /* ValueRecord definition--for the
749 * second glyph of the pair--may be
752 classDef1; /* Offset to ClassDef table--from
753 * beginning of PairPos subtable--for
754 * the first glyph of the pair */
756 classDef2; /* Offset to ClassDef table--from
757 * beginning of PairPos subtable--for
758 * the second glyph of the pair */
759 USHORT class1Count; /* Number of classes in ClassDef1
760 * table--includes Class0 */
761 USHORT class2Count; /* Number of classes in ClassDef2
762 * table--includes Class0 */
763 ValueRecord values; /* Matrix of value pairs:
764 * class1-major, class2-minor,
765 * Each entry has value1 and value2 */
767 DEFINE_SIZE_VAR (16, ValueRecord);
772 friend struct PosLookupSubTable;
775 inline bool apply (hb_apply_context_t *context) const
779 case 1: return u.format1->apply (context);
780 case 2: return u.format2->apply (context);
781 default:return false;
785 inline bool sanitize (hb_sanitize_context_t *context) {
787 if (!u.format.sanitize (context)) return false;
789 case 1: return u.format1->sanitize (context);
790 case 2: return u.format2->sanitize (context);
797 USHORT format; /* Format identifier */
798 PairPosFormat1 format1[VAR];
799 PairPosFormat2 format2[VAR];
804 struct EntryExitRecord
806 friend struct CursivePosFormat1;
808 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
810 return entryAnchor.sanitize (context, base)
811 && exitAnchor.sanitize (context, base);
816 entryAnchor; /* Offset to EntryAnchor table--from
817 * beginning of CursivePos
818 * subtable--may be NULL */
820 exitAnchor; /* Offset to ExitAnchor table--from
821 * beginning of CursivePos
822 * subtable--may be NULL */
824 DEFINE_SIZE_STATIC (4);
827 struct CursivePosFormat1
829 friend struct CursivePos;
832 inline bool apply (hb_apply_context_t *context) const
835 /* Now comes the messiest part of the whole OpenType
836 specification. At first glance, cursive connections seem easy
837 to understand, but there are pitfalls! The reason is that
838 the specs don't mention how to compute the advance values
839 resp. glyph offsets. I was told it would be an omission, to
840 be fixed in the next OpenType version... Again many thanks to
841 Andrei Burago <andreib@microsoft.com> for clarifications.
843 Consider the following example:
856 glyph1: advance width = 12
859 glyph2: advance width = 11
862 LSB is 1 for both glyphs (so the boxes drawn above are glyph
863 bboxes). Writing direction is R2L; `0' denotes the glyph's
866 Now the surprising part: The advance width of the *left* glyph
867 (resp. of the *bottom* glyph) will be modified, no matter
868 whether the writing direction is L2R or R2L (resp. T2B or
869 B2T)! This assymetry is caused by the fact that the glyph's
870 coordinate origin is always the lower left corner for all
873 Continuing the above example, we can compute the new
874 (horizontal) advance width of glyph2 as
878 and the new vertical offset of glyph2 as
883 Vertical writing direction is far more complicated:
885 a) Assuming that we recompute the advance height of the lower glyph:
892 yadv2 | 0+--+------+ -- BSB1 --
895 BSB2 -- 0+--------+ --
898 glyph1: advance height = 6
901 glyph2: advance height = 7
904 TSB is 1 for both glyphs; writing direction is T2B.
907 BSB1 = yadv1 - (TSB1 + ymax1)
908 BSB2 = yadv2 - (TSB2 + ymax2)
911 vertical advance width of glyph2
912 = y_offset + BSB2 - BSB1
913 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
914 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
915 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
918 b) Assuming that we recompute the advance height of the upper glyph:
923 TSB2 -- +-----+--+ 1 | yadv1 ymax1
925 yadv2 | 0+--+------+ -- --
926 ymax2 | 2 | -- y_offset
931 glyph1: advance height = 6
934 glyph2: advance height = 7
937 TSB is 1 for both glyphs; writing direction is T2B.
941 vertical advance width of glyph2
942 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
943 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
946 Comparing a) with b) shows that b) is easier to compute. I'll wait
947 for a reply from Andrei to see what should really be implemented...
949 Since horizontal advance widths or vertical advance heights
950 can be used alone but not together, no ambiguity occurs. */
952 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
953 hb_codepoint_t last_pos = gpi->last;
954 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
956 /* We don't handle mark glyphs here. */
957 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
960 unsigned int index = (this+coverage) (IN_CURGLYPH ());
961 if (likely (index == NOT_COVERED))
964 const EntryExitRecord &record = entryExitRecord[index];
966 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
969 hb_position_t entry_x, entry_y;
970 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
974 if (context->buffer->direction == HB_DIRECTION_RTL)
976 /* advance is absolute, not relative */
977 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
981 /* advance is absolute, not relative */
982 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
985 if (context->lookup_flag & LookupFlag::RightToLeft)
987 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
988 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
992 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
993 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
997 if (record.exitAnchor)
999 gpi->last = context->buffer->in_pos;
1000 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
1003 context->buffer->in_pos++;
1007 inline bool sanitize (hb_sanitize_context_t *context) {
1009 return coverage.sanitize (context, this)
1010 && entryExitRecord.sanitize (context, this);
1014 USHORT format; /* Format identifier--format = 1 */
1016 coverage; /* Offset to Coverage table--from
1017 * beginning of subtable */
1018 ArrayOf<EntryExitRecord>
1019 entryExitRecord; /* Array of EntryExit records--in
1020 * Coverage Index order */
1022 DEFINE_SIZE_STATIC (6);
1027 friend struct PosLookupSubTable;
1030 inline bool apply (hb_apply_context_t *context) const
1034 case 1: return u.format1->apply (context);
1035 default:return false;
1039 inline bool sanitize (hb_sanitize_context_t *context) {
1041 if (!u.format.sanitize (context)) return false;
1043 case 1: return u.format1->sanitize (context);
1044 default:return true;
1050 USHORT format; /* Format identifier */
1051 CursivePosFormat1 format1[VAR];
1056 typedef AnchorMatrix BaseArray; /* base-major--
1057 * in order of BaseCoverage Index--,
1059 * ordered by class--zero-based. */
1061 struct MarkBasePosFormat1
1063 friend struct MarkBasePos;
1066 inline bool apply (hb_apply_context_t *context) const
1069 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1070 if (likely (mark_index == NOT_COVERED))
1073 /* now we search backwards for a non-mark glyph */
1074 unsigned int property;
1075 unsigned int j = context->buffer->in_pos;
1081 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1083 /* The following assertion is too strong, so we've disabled it. */
1084 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1087 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1088 if (base_index == NOT_COVERED)
1091 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1094 inline bool sanitize (hb_sanitize_context_t *context) {
1096 return context->check_struct (this)
1097 && markCoverage.sanitize (context, this)
1098 && baseCoverage.sanitize (context, this)
1099 && markArray.sanitize (context, this)
1100 && likely (baseArray.sanitize (context, CharP(this), (unsigned int) classCount));
1104 USHORT format; /* Format identifier--format = 1 */
1106 markCoverage; /* Offset to MarkCoverage table--from
1107 * beginning of MarkBasePos subtable */
1109 baseCoverage; /* Offset to BaseCoverage table--from
1110 * beginning of MarkBasePos subtable */
1111 USHORT classCount; /* Number of classes defined for marks */
1113 markArray; /* Offset to MarkArray table--from
1114 * beginning of MarkBasePos subtable */
1116 baseArray; /* Offset to BaseArray table--from
1117 * beginning of MarkBasePos subtable */
1119 DEFINE_SIZE_STATIC (12);
1124 friend struct PosLookupSubTable;
1127 inline bool apply (hb_apply_context_t *context) const
1131 case 1: return u.format1->apply (context);
1132 default:return false;
1136 inline bool sanitize (hb_sanitize_context_t *context) {
1138 if (!u.format.sanitize (context)) return false;
1140 case 1: return u.format1->sanitize (context);
1141 default:return true;
1147 USHORT format; /* Format identifier */
1148 MarkBasePosFormat1 format1[VAR];
1153 typedef AnchorMatrix LigatureAttach; /* component-major--
1154 * in order of writing direction--,
1156 * ordered by class--zero-based. */
1158 typedef OffsetListOf<LigatureAttach> LigatureArray;
1159 /* Array of LigatureAttach
1161 * LigatureCoverage Index */
1163 struct MarkLigPosFormat1
1165 friend struct MarkLigPos;
1168 inline bool apply (hb_apply_context_t *context) const
1171 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1172 if (likely (mark_index == NOT_COVERED))
1175 /* now we search backwards for a non-mark glyph */
1176 unsigned int property;
1177 unsigned int j = context->buffer->in_pos;
1183 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1185 /* The following assertion is too strong, so we've disabled it. */
1186 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1189 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1190 if (lig_index == NOT_COVERED)
1193 const LigatureArray& lig_array = this+ligatureArray;
1194 const LigatureAttach& lig_attach = lig_array[lig_index];
1196 /* Find component to attach to */
1197 unsigned int comp_count = lig_attach.rows;
1198 if (unlikely (!comp_count))
1200 unsigned int comp_index;
1201 /* We must now check whether the ligature ID of the current mark glyph
1202 * is identical to the ligature ID of the found ligature. If yes, we
1203 * can directly use the component index. If not, we attach the mark
1204 * glyph to the last component of the ligature. */
1205 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1207 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1208 if (comp_index >= comp_count)
1209 comp_index = comp_count - 1;
1212 comp_index = comp_count - 1;
1214 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1217 inline bool sanitize (hb_sanitize_context_t *context) {
1219 return context->check_struct (this)
1220 && markCoverage.sanitize (context, this)
1221 && ligatureCoverage.sanitize (context, this)
1222 && markArray.sanitize (context, this)
1223 && likely (ligatureArray.sanitize (context, CharP(this), (unsigned int) classCount));
1227 USHORT format; /* Format identifier--format = 1 */
1229 markCoverage; /* Offset to Mark Coverage table--from
1230 * beginning of MarkLigPos subtable */
1232 ligatureCoverage; /* Offset to Ligature Coverage
1233 * table--from beginning of MarkLigPos
1235 USHORT classCount; /* Number of defined mark classes */
1237 markArray; /* Offset to MarkArray table--from
1238 * beginning of MarkLigPos subtable */
1239 OffsetTo<LigatureArray>
1240 ligatureArray; /* Offset to LigatureArray table--from
1241 * beginning of MarkLigPos subtable */
1243 DEFINE_SIZE_STATIC (12);
1248 friend struct PosLookupSubTable;
1251 inline bool apply (hb_apply_context_t *context) const
1255 case 1: return u.format1->apply (context);
1256 default:return false;
1260 inline bool sanitize (hb_sanitize_context_t *context) {
1262 if (!u.format.sanitize (context)) return false;
1264 case 1: return u.format1->sanitize (context);
1265 default:return true;
1271 USHORT format; /* Format identifier */
1272 MarkLigPosFormat1 format1[VAR];
1277 typedef AnchorMatrix Mark2Array; /* mark2-major--
1278 * in order of Mark2Coverage Index--,
1280 * ordered by class--zero-based. */
1282 struct MarkMarkPosFormat1
1284 friend struct MarkMarkPos;
1287 inline bool apply (hb_apply_context_t *context) const
1290 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1291 if (likely (mark1_index == NOT_COVERED))
1294 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1295 unsigned int property;
1296 unsigned int j = context->buffer->in_pos;
1302 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1304 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1307 /* Two marks match only if they belong to the same base, or same component
1308 * of the same ligature. That is, the component numbers must match, and
1309 * if those are non-zero, the ligid number should also match. */
1310 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1311 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1314 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1315 if (mark2_index == NOT_COVERED)
1318 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1321 inline bool sanitize (hb_sanitize_context_t *context) {
1323 return context->check_struct (this)
1324 && mark1Coverage.sanitize (context, this)
1325 && mark2Coverage.sanitize (context, this)
1326 && mark1Array.sanitize (context, this)
1327 && likely (mark2Array.sanitize (context, CharP(this), (unsigned int) classCount));
1331 USHORT format; /* Format identifier--format = 1 */
1333 mark1Coverage; /* Offset to Combining Mark1 Coverage
1334 * table--from beginning of MarkMarkPos
1337 mark2Coverage; /* Offset to Combining Mark2 Coverage
1338 * table--from beginning of MarkMarkPos
1340 USHORT classCount; /* Number of defined mark classes */
1342 mark1Array; /* Offset to Mark1Array table--from
1343 * beginning of MarkMarkPos subtable */
1344 OffsetTo<Mark2Array>
1345 mark2Array; /* Offset to Mark2Array table--from
1346 * beginning of MarkMarkPos subtable */
1348 DEFINE_SIZE_STATIC (12);
1353 friend struct PosLookupSubTable;
1356 inline bool apply (hb_apply_context_t *context) const
1360 case 1: return u.format1->apply (context);
1361 default:return false;
1365 inline bool sanitize (hb_sanitize_context_t *context) {
1367 if (!u.format.sanitize (context)) return false;
1369 case 1: return u.format1->sanitize (context);
1370 default:return true;
1376 USHORT format; /* Format identifier */
1377 MarkMarkPosFormat1 format1[VAR];
1382 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1384 struct ContextPos : Context
1386 friend struct PosLookupSubTable;
1389 inline bool apply (hb_apply_context_t *context) const
1392 return Context::apply (context, position_lookup);
1396 struct ChainContextPos : ChainContext
1398 friend struct PosLookupSubTable;
1401 inline bool apply (hb_apply_context_t *context) const
1404 return ChainContext::apply (context, position_lookup);
1409 struct ExtensionPos : Extension
1411 friend struct PosLookupSubTable;
1414 inline const struct PosLookupSubTable& get_subtable (void) const
1416 unsigned int offset = get_offset ();
1417 if (unlikely (!offset)) return Null(PosLookupSubTable);
1418 return StructAtOffset<PosLookupSubTable> (*this, offset);
1421 inline bool apply (hb_apply_context_t *context) const;
1423 inline bool sanitize (hb_sanitize_context_t *context);
1433 struct PosLookupSubTable
1435 friend struct PosLookup;
1449 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1452 switch (lookup_type) {
1453 case Single: return u.single->apply (context);
1454 case Pair: return u.pair->apply (context);
1455 case Cursive: return u.cursive->apply (context);
1456 case MarkBase: return u.markBase->apply (context);
1457 case MarkLig: return u.markLig->apply (context);
1458 case MarkMark: return u.markMark->apply (context);
1459 case Context: return u.context->apply (context);
1460 case ChainContext: return u.chainContext->apply (context);
1461 case Extension: return u.extension->apply (context);
1462 default:return false;
1466 inline bool sanitize (hb_sanitize_context_t *context) {
1468 if (!u.format.sanitize (context)) return false;
1470 case Single: return u.single->sanitize (context);
1471 case Pair: return u.pair->sanitize (context);
1472 case Cursive: return u.cursive->sanitize (context);
1473 case MarkBase: return u.markBase->sanitize (context);
1474 case MarkLig: return u.markLig->sanitize (context);
1475 case MarkMark: return u.markMark->sanitize (context);
1476 case Context: return u.context->sanitize (context);
1477 case ChainContext: return u.chainContext->sanitize (context);
1478 case Extension: return u.extension->sanitize (context);
1479 default:return true;
1486 SinglePos single[VAR];
1488 CursivePos cursive[VAR];
1489 MarkBasePos markBase[VAR];
1490 MarkLigPos markLig[VAR];
1491 MarkMarkPos markMark[VAR];
1492 ContextPos context[VAR];
1493 ChainContextPos chainContext[VAR];
1494 ExtensionPos extension[VAR];
1499 struct PosLookup : Lookup
1501 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1502 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1504 inline bool apply_once (hb_ot_layout_context_t *layout,
1505 hb_buffer_t *buffer,
1506 unsigned int context_length,
1507 unsigned int nesting_level_left) const
1509 unsigned int lookup_type = get_type ();
1510 hb_apply_context_t context[1] = {{0}};
1512 context->layout = layout;
1513 context->buffer = buffer;
1514 context->context_length = context_length;
1515 context->nesting_level_left = nesting_level_left;
1516 context->lookup_flag = get_flag ();
1518 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1521 for (unsigned int i = 0; i < get_subtable_count (); i++)
1522 if (get_subtable (i).apply (context, lookup_type))
1528 inline bool apply_string (hb_ot_layout_context_t *layout,
1529 hb_buffer_t *buffer,
1530 hb_mask_t mask) const
1533 #define BUFFER buffer
1536 if (unlikely (!buffer->in_length))
1539 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1542 while (buffer->in_pos < buffer->in_length)
1545 if (~IN_MASK (buffer->in_pos) & mask)
1547 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1553 /* Contrary to properties defined in GDEF, user-defined properties
1554 will always stop a possible cursive positioning. */
1555 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1565 inline bool sanitize (hb_sanitize_context_t *context) {
1567 if (unlikely (!Lookup::sanitize (context))) return false;
1568 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1569 return list.sanitize (context, this);
1573 typedef OffsetListOf<PosLookup> PosLookupList;
1579 struct GPOS : GSUBGPOS
1581 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1583 inline const PosLookup& get_lookup (unsigned int i) const
1584 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1586 inline bool position_lookup (hb_ot_layout_context_t *layout,
1587 hb_buffer_t *buffer,
1588 unsigned int lookup_index,
1589 hb_mask_t mask) const
1590 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1592 inline bool sanitize (hb_sanitize_context_t *context) {
1594 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1595 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1596 return list.sanitize (context, this);
1599 DEFINE_SIZE_STATIC (10);
1603 /* Out-of-class implementation for methods recursing */
1605 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1608 return get_subtable ().apply (context, get_type ());
1611 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1614 if (unlikely (!Extension::sanitize (context))) return false;
1615 unsigned int offset = get_offset ();
1616 if (unlikely (!offset)) return true;
1617 return StructAtOffset<PosLookupSubTable> (*this, offset).sanitize (context);
1620 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1622 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1623 const PosLookup &l = gpos.get_lookup (lookup_index);
1625 if (unlikely (context->nesting_level_left == 0))
1628 if (unlikely (context->context_length < 1))
1631 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1635 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */