2 * Copyright (C) 2007,2008,2009 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];
44 ASSERT_SIZE_VAR (ValueRecord, 0, Value);
46 struct ValueFormat : USHORT
50 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
51 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
52 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
53 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
54 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
55 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
56 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
57 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
58 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
59 reserved = 0xF000, /* For future use */
61 devices = 0x00F0 /* Mask for having any Device table */
64 /* All fields are options. Only those available advance the value pointer. */
66 SHORT xPlacement; /* Horizontal adjustment for
67 * placement--in design units */
68 SHORT yPlacement; /* Vertical adjustment for
69 * placement--in design units */
70 SHORT xAdvance; /* Horizontal adjustment for
71 * advance--in design units (only used
72 * for horizontal writing) */
73 SHORT yAdvance; /* Vertical adjustment for advance--in
74 * design units (only used for vertical
76 Offset xPlaDevice; /* Offset to Device table for
77 * horizontal placement--measured from
78 * beginning of PosTable (may be NULL) */
79 Offset yPlaDevice; /* Offset to Device table for vertical
80 * placement--measured from beginning
81 * of PosTable (may be NULL) */
82 Offset xAdvDevice; /* Offset to Device table for
83 * horizontal advance--measured from
84 * beginning of PosTable (may be NULL) */
85 Offset yAdvDevice; /* Offset to Device table for vertical
86 * advance--measured from beginning of
87 * PosTable (may be NULL) */
90 inline unsigned int get_len () const
91 { return _hb_popcount32 ((unsigned int) *this); }
92 inline unsigned int get_size () const
93 { return get_len () * Value::get_size (); }
95 void apply_value (hb_ot_layout_context_t *layout,
98 hb_internal_glyph_position_t *glyph_pos) const
100 unsigned int x_ppem, y_ppem;
101 hb_16dot16_t x_scale, y_scale;
102 unsigned int format = *this;
106 x_scale = layout->font->x_scale;
107 y_scale = layout->font->y_scale;
108 /* design units -> fractional pixel */
109 if (format & xPlacement) glyph_pos->x_offset += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
110 if (format & yPlacement) glyph_pos->y_offset += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
111 if (format & xAdvance) glyph_pos->x_advance += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
112 if (format & yAdvance) glyph_pos->y_advance += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
114 if (!has_device ()) return;
116 x_ppem = layout->font->x_ppem;
117 y_ppem = layout->font->y_ppem;
119 if (!x_ppem && !y_ppem) return;
121 /* pixel -> fractional pixel */
122 if (format & xPlaDevice) {
123 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
125 if (format & yPlaDevice) {
126 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
128 if (format & xAdvDevice) {
129 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
131 if (format & yAdvDevice) {
132 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
137 inline bool sanitize_value_devices (hb_sanitize_context_t *context, void *base, const Value *values) {
138 unsigned int format = *this;
140 if (format & xPlacement) values++;
141 if (format & yPlacement) values++;
142 if (format & xAdvance) values++;
143 if (format & yAdvance) values++;
145 if ((format & xPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
146 if ((format & yPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
147 if ((format & xAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
148 if ((format & yAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
155 inline bool has_device () const {
156 unsigned int format = *this;
157 return (format & devices) != 0;
160 inline bool sanitize_value (hb_sanitize_context_t *context, void *base, const Value *values) {
162 return context->check_range (values, get_size ())
163 && (!has_device () || sanitize_value_devices (context, base, values));
166 inline bool sanitize_values (hb_sanitize_context_t *context, void *base, const Value *values, unsigned int count) {
168 unsigned int len = get_len ();
170 if (!context->check_array (values, get_size (), count)) return false;
172 if (!has_device ()) return true;
174 for (unsigned int i = 0; i < count; i++) {
175 if (!sanitize_value_devices (context, base, values))
183 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
184 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *context, void *base, const Value *values, unsigned int count, unsigned int stride) {
187 if (!has_device ()) return true;
189 for (unsigned int i = 0; i < count; i++) {
190 if (!sanitize_value_devices (context, base, values))
198 ASSERT_SIZE (ValueFormat, 2);
203 friend struct Anchor;
206 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
207 hb_position_t *x, hb_position_t *y) const
209 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
210 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
213 inline bool sanitize (hb_sanitize_context_t *context) {
215 return SANITIZE_SELF ();
219 USHORT format; /* Format identifier--format = 1 */
220 SHORT xCoordinate; /* Horizontal value--in design units */
221 SHORT yCoordinate; /* Vertical value--in design units */
223 ASSERT_SIZE (AnchorFormat1, 6);
227 friend struct Anchor;
230 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
231 hb_position_t *x, hb_position_t *y) const
233 unsigned int x_ppem = layout->font->x_ppem;
234 unsigned int y_ppem = layout->font->y_ppem;
235 hb_position_t cx, cy;
238 if (x_ppem || y_ppem)
239 ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
240 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
241 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
244 inline bool sanitize (hb_sanitize_context_t *context) {
246 return SANITIZE_SELF ();
250 USHORT format; /* Format identifier--format = 2 */
251 SHORT xCoordinate; /* Horizontal value--in design units */
252 SHORT yCoordinate; /* Vertical value--in design units */
253 USHORT anchorPoint; /* Index to glyph contour point */
255 ASSERT_SIZE (AnchorFormat2, 8);
259 friend struct Anchor;
262 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
263 hb_position_t *x, hb_position_t *y) const
265 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
266 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
268 /* pixel -> fractional pixel */
269 if (layout->font->x_ppem)
270 *x += (this+xDeviceTable).get_delta (layout->font->x_ppem) << 16;
271 if (layout->font->y_ppem)
272 *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) << 16;
275 inline bool sanitize (hb_sanitize_context_t *context) {
277 return SANITIZE_SELF ()
278 && SANITIZE_WITH_BASE (this, xDeviceTable)
279 && SANITIZE_WITH_BASE (this, yDeviceTable);
283 USHORT format; /* Format identifier--format = 3 */
284 SHORT xCoordinate; /* Horizontal value--in design units */
285 SHORT yCoordinate; /* Vertical value--in design units */
287 xDeviceTable; /* Offset to Device table for X
288 * coordinate-- from beginning of
289 * Anchor table (may be NULL) */
291 yDeviceTable; /* Offset to Device table for Y
292 * coordinate-- from beginning of
293 * Anchor table (may be NULL) */
295 ASSERT_SIZE (AnchorFormat3, 10);
299 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
300 hb_position_t *x, hb_position_t *y) const
304 case 1: u.format1->get_anchor (layout, glyph_id, x, y); return;
305 case 2: u.format2->get_anchor (layout, glyph_id, x, y); return;
306 case 3: u.format3->get_anchor (layout, glyph_id, x, y); return;
311 inline bool sanitize (hb_sanitize_context_t *context) {
313 if (!SANITIZE (u.format)) return false;
315 case 1: return u.format1->sanitize (context);
316 case 2: return u.format2->sanitize (context);
317 case 3: return u.format3->sanitize (context);
324 USHORT format; /* Format identifier */
325 AnchorFormat1 format1[VAR];
326 AnchorFormat2 format2[VAR];
327 AnchorFormat3 format3[VAR];
334 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
335 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
336 return this+matrix[row * cols + col];
339 inline bool sanitize (hb_sanitize_context_t *context, unsigned int cols) {
341 if (!SANITIZE_SELF ()) return false;
342 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
343 unsigned int count = rows * cols;
344 if (!context->check_array (matrix, matrix[0].get_size (), count)) return false;
345 for (unsigned int i = 0; i < count; i++)
346 if (!SANITIZE_WITH_BASE (this, matrix[i])) return false;
350 USHORT rows; /* Number of rows */
353 matrix[VAR]; /* Matrix of offsets to Anchor tables--
354 * from beginning of AnchorMatrix table */
356 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
361 friend struct MarkArray;
363 static inline unsigned int get_size () { return sizeof (MarkRecord); }
365 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
367 return SANITIZE_SELF ()
368 && SANITIZE_WITH_BASE (base, markAnchor);
372 USHORT klass; /* Class defined for this mark */
374 markAnchor; /* Offset to Anchor table--from
375 * beginning of MarkArray table */
377 ASSERT_SIZE (MarkRecord, 4);
381 inline bool apply (hb_apply_context_t *context,
382 unsigned int mark_index, unsigned int glyph_index,
383 const AnchorMatrix &anchors, unsigned int class_count,
384 unsigned int glyph_pos) const
387 const MarkRecord &record = markRecord[mark_index];
388 unsigned int mark_class = record.klass;
390 const Anchor& mark_anchor = this + record.markAnchor;
391 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
393 hb_position_t mark_x, mark_y, base_x, base_y;
395 mark_anchor.get_anchor (context->layout, IN_CURGLYPH (), &mark_x, &mark_y);
396 glyph_anchor.get_anchor (context->layout, IN_GLYPH (glyph_pos), &base_x, &base_y);
398 hb_internal_glyph_position_t *o = POSITION (context->buffer->in_pos);
401 o->x_offset = base_x - mark_x;
402 o->y_offset = base_y - mark_y;
403 o->back = context->buffer->in_pos - glyph_pos;
405 context->buffer->in_pos++;
409 inline bool sanitize (hb_sanitize_context_t *context) {
411 return SANITIZE_WITH_BASE (this, markRecord);
416 markRecord; /* Array of MarkRecords--in Coverage order */
418 ASSERT_SIZE (MarkArray, 2);
423 struct SinglePosFormat1
425 friend struct SinglePos;
428 inline bool apply (hb_apply_context_t *context) const
431 unsigned int index = (this+coverage) (IN_CURGLYPH ());
432 if (likely (index == NOT_COVERED))
435 valueFormat.apply_value (context->layout, CharP(this), values, CURPOSITION ());
437 context->buffer->in_pos++;
441 inline bool sanitize (hb_sanitize_context_t *context) {
443 return SANITIZE_SELF ()
444 && SANITIZE_WITH_BASE (this, coverage)
445 && valueFormat.sanitize_value (context, CharP(this), values);
449 USHORT format; /* Format identifier--format = 1 */
451 coverage; /* Offset to Coverage table--from
452 * beginning of subtable */
453 ValueFormat valueFormat; /* Defines the types of data in the
455 ValueRecord values; /* Defines positioning
456 * value(s)--applied to all glyphs in
457 * the Coverage table */
459 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
461 struct SinglePosFormat2
463 friend struct SinglePos;
466 inline bool apply (hb_apply_context_t *context) const
469 unsigned int index = (this+coverage) (IN_CURGLYPH ());
470 if (likely (index == NOT_COVERED))
473 if (likely (index >= valueCount))
476 valueFormat.apply_value (context->layout, CharP(this),
477 &values[index * valueFormat.get_len ()],
480 context->buffer->in_pos++;
484 inline bool sanitize (hb_sanitize_context_t *context) {
486 return SANITIZE_SELF ()
487 && SANITIZE_WITH_BASE (this, coverage)
488 && valueFormat.sanitize_values (context, CharP(this), values, valueCount);
492 USHORT format; /* Format identifier--format = 2 */
494 coverage; /* Offset to Coverage table--from
495 * beginning of subtable */
496 ValueFormat valueFormat; /* Defines the types of data in the
498 USHORT valueCount; /* Number of ValueRecords */
499 ValueRecord values; /* Array of ValueRecords--positioning
500 * values applied to glyphs */
502 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
506 friend struct PosLookupSubTable;
509 inline bool apply (hb_apply_context_t *context) const
513 case 1: return u.format1->apply (context);
514 case 2: return u.format2->apply (context);
515 default:return false;
519 inline bool sanitize (hb_sanitize_context_t *context) {
521 if (!SANITIZE (u.format)) return false;
523 case 1: return u.format1->sanitize (context);
524 case 2: return u.format2->sanitize (context);
531 USHORT format; /* Format identifier */
532 SinglePosFormat1 format1[VAR];
533 SinglePosFormat2 format2[VAR];
538 struct PairValueRecord
540 friend struct PairPosFormat1;
543 GlyphID secondGlyph; /* GlyphID of second glyph in the
544 * pair--first glyph is listed in the
546 ValueRecord values; /* Positioning data for the first glyph
547 * followed by for second glyph */
549 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
553 friend struct PairPosFormat1;
555 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
556 inline bool sanitize (hb_sanitize_context_t *context, unsigned int format_len) {
558 if (!SANITIZE_SELF ()) return false;
559 unsigned int count = (1 + format_len) * len;
560 return context->check_array (array, USHORT::get_size (), count);
564 USHORT len; /* Number of PairValueRecords */
566 array[VAR]; /* Array of PairValueRecords--ordered
567 * by GlyphID of the second glyph */
569 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
571 struct PairPosFormat1
573 friend struct PairPos;
576 inline bool apply (hb_apply_context_t *context) const
579 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
580 if (unlikely (context->buffer->in_pos + 2 > end))
583 unsigned int index = (this+coverage) (IN_CURGLYPH ());
584 if (likely (index == NOT_COVERED))
587 unsigned int j = context->buffer->in_pos + 1;
588 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
590 if (unlikely (j == end))
595 unsigned int len1 = valueFormat1.get_len ();
596 unsigned int len2 = valueFormat2.get_len ();
597 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
599 const PairSet &pair_set = this+pairSet[index];
600 unsigned int count = pair_set.len;
601 const PairValueRecord *record = pair_set.array;
602 for (unsigned int i = 0; i < count; i++)
604 if (IN_GLYPH (j) == record->secondGlyph)
606 valueFormat1.apply_value (context->layout, CharP(this), &record->values[0], CURPOSITION ());
607 valueFormat2.apply_value (context->layout, CharP(this), &record->values[len1], POSITION (j));
610 context->buffer->in_pos = j;
613 record = &StructAtOffset<PairValueRecord> (*record, record_size);
619 inline bool sanitize (hb_sanitize_context_t *context) {
622 unsigned int len1 = valueFormat1.get_len ();
623 unsigned int len2 = valueFormat2.get_len ();
625 if (!(SANITIZE_SELF ()
626 && SANITIZE_WITH_BASE (this, coverage)
627 && likely (pairSet.sanitize (context, CharP(this), len1 + len2)))) return false;
629 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
631 unsigned int stride = 1 + len1 + len2;
632 unsigned int count1 = pairSet.len;
633 for (unsigned int i = 0; i < count1; i++)
635 const PairSet &pair_set = this+pairSet[i];
637 unsigned int count2 = pair_set.len;
638 const PairValueRecord *record = pair_set.array;
639 if (!(valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &record->values[0], count2, stride) &&
640 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &record->values[len1], count2, stride)))
648 USHORT format; /* Format identifier--format = 1 */
650 coverage; /* Offset to Coverage table--from
651 * beginning of subtable */
652 ValueFormat valueFormat1; /* Defines the types of data in
653 * ValueRecord1--for the first glyph
654 * in the pair--may be zero (0) */
655 ValueFormat valueFormat2; /* Defines the types of data in
656 * ValueRecord2--for the second glyph
657 * in the pair--may be zero (0) */
658 OffsetArrayOf<PairSet>
659 pairSet; /* Array of PairSet tables
660 * ordered by Coverage Index */
662 ASSERT_SIZE (PairPosFormat1, 10);
664 struct PairPosFormat2
666 friend struct PairPos;
669 inline bool apply (hb_apply_context_t *context) const
672 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
673 if (unlikely (context->buffer->in_pos + 2 > end))
676 unsigned int index = (this+coverage) (IN_CURGLYPH ());
677 if (likely (index == NOT_COVERED))
680 unsigned int j = context->buffer->in_pos + 1;
681 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
683 if (unlikely (j == end))
688 unsigned int len1 = valueFormat1.get_len ();
689 unsigned int len2 = valueFormat2.get_len ();
690 unsigned int record_len = len1 + len2;
692 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
693 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
694 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
697 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
698 valueFormat1.apply_value (context->layout, CharP(this), v, CURPOSITION ());
699 valueFormat2.apply_value (context->layout, CharP(this), v + len1, POSITION (j));
703 context->buffer->in_pos = j;
708 inline bool sanitize (hb_sanitize_context_t *context) {
710 if (!(SANITIZE_SELF ()
711 && SANITIZE_WITH_BASE (this, coverage)
712 && SANITIZE_WITH_BASE (this, classDef1)
713 && SANITIZE_WITH_BASE (this, classDef2))) return false;
715 unsigned int len1 = valueFormat1.get_len ();
716 unsigned int len2 = valueFormat2.get_len ();
717 unsigned int stride = len1 + len2;
718 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
719 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
720 return context->check_array (values, record_size, count) &&
721 valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &values[0], count, stride) &&
722 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &values[len1], count, stride);
726 USHORT format; /* Format identifier--format = 2 */
728 coverage; /* Offset to Coverage table--from
729 * beginning of subtable */
730 ValueFormat valueFormat1; /* ValueRecord definition--for the
731 * first glyph of the pair--may be zero
733 ValueFormat valueFormat2; /* ValueRecord definition--for the
734 * second glyph of the pair--may be
737 classDef1; /* Offset to ClassDef table--from
738 * beginning of PairPos subtable--for
739 * the first glyph of the pair */
741 classDef2; /* Offset to ClassDef table--from
742 * beginning of PairPos subtable--for
743 * the second glyph of the pair */
744 USHORT class1Count; /* Number of classes in ClassDef1
745 * table--includes Class0 */
746 USHORT class2Count; /* Number of classes in ClassDef2
747 * table--includes Class0 */
748 ValueRecord values; /* Matrix of value pairs:
749 * class1-major, class2-minor,
750 * Each entry has value1 and value2 */
752 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
756 friend struct PosLookupSubTable;
759 inline bool apply (hb_apply_context_t *context) const
763 case 1: return u.format1->apply (context);
764 case 2: return u.format2->apply (context);
765 default:return false;
769 inline bool sanitize (hb_sanitize_context_t *context) {
771 if (!SANITIZE (u.format)) return false;
773 case 1: return u.format1->sanitize (context);
774 case 2: return u.format2->sanitize (context);
781 USHORT format; /* Format identifier */
782 PairPosFormat1 format1[VAR];
783 PairPosFormat2 format2[VAR];
788 struct EntryExitRecord
790 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
792 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
794 return SANITIZE_WITH_BASE (base, entryAnchor)
795 && SANITIZE_WITH_BASE (base, exitAnchor);
799 entryAnchor; /* Offset to EntryAnchor table--from
800 * beginning of CursivePos
801 * subtable--may be NULL */
803 exitAnchor; /* Offset to ExitAnchor table--from
804 * beginning of CursivePos
805 * subtable--may be NULL */
807 ASSERT_SIZE (EntryExitRecord, 4);
809 struct CursivePosFormat1
811 friend struct CursivePos;
814 inline bool apply (hb_apply_context_t *context) const
817 /* Now comes the messiest part of the whole OpenType
818 specification. At first glance, cursive connections seem easy
819 to understand, but there are pitfalls! The reason is that
820 the specs don't mention how to compute the advance values
821 resp. glyph offsets. I was told it would be an omission, to
822 be fixed in the next OpenType version... Again many thanks to
823 Andrei Burago <andreib@microsoft.com> for clarifications.
825 Consider the following example:
838 glyph1: advance width = 12
841 glyph2: advance width = 11
844 LSB is 1 for both glyphs (so the boxes drawn above are glyph
845 bboxes). Writing direction is R2L; `0' denotes the glyph's
848 Now the surprising part: The advance width of the *left* glyph
849 (resp. of the *bottom* glyph) will be modified, no matter
850 whether the writing direction is L2R or R2L (resp. T2B or
851 B2T)! This assymetry is caused by the fact that the glyph's
852 coordinate origin is always the lower left corner for all
855 Continuing the above example, we can compute the new
856 (horizontal) advance width of glyph2 as
860 and the new vertical offset of glyph2 as
865 Vertical writing direction is far more complicated:
867 a) Assuming that we recompute the advance height of the lower glyph:
874 yadv2 | 0+--+------+ -- BSB1 --
877 BSB2 -- 0+--------+ --
880 glyph1: advance height = 6
883 glyph2: advance height = 7
886 TSB is 1 for both glyphs; writing direction is T2B.
889 BSB1 = yadv1 - (TSB1 + ymax1)
890 BSB2 = yadv2 - (TSB2 + ymax2)
893 vertical advance width of glyph2
894 = y_offset + BSB2 - BSB1
895 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
896 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
897 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
900 b) Assuming that we recompute the advance height of the upper glyph:
905 TSB2 -- +-----+--+ 1 | yadv1 ymax1
907 yadv2 | 0+--+------+ -- --
908 ymax2 | 2 | -- y_offset
913 glyph1: advance height = 6
916 glyph2: advance height = 7
919 TSB is 1 for both glyphs; writing direction is T2B.
923 vertical advance width of glyph2
924 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
925 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
928 Comparing a) with b) shows that b) is easier to compute. I'll wait
929 for a reply from Andrei to see what should really be implemented...
931 Since horizontal advance widths or vertical advance heights
932 can be used alone but not together, no ambiguity occurs. */
934 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
935 hb_codepoint_t last_pos = gpi->last;
936 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
938 /* We don't handle mark glyphs here. */
939 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
942 unsigned int index = (this+coverage) (IN_CURGLYPH ());
943 if (likely (index == NOT_COVERED))
946 const EntryExitRecord &record = entryExitRecord[index];
948 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
951 hb_position_t entry_x, entry_y;
952 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
956 if (context->buffer->direction == HB_DIRECTION_RTL)
958 /* advance is absolute, not relative */
959 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
963 /* advance is absolute, not relative */
964 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
967 if (context->lookup_flag & LookupFlag::RightToLeft)
969 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
970 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
974 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
975 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
979 if (record.exitAnchor)
981 gpi->last = context->buffer->in_pos;
982 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
985 context->buffer->in_pos++;
989 inline bool sanitize (hb_sanitize_context_t *context) {
991 return SANITIZE_WITH_BASE (this, coverage)
992 && SANITIZE_WITH_BASE (this, entryExitRecord);
996 USHORT format; /* Format identifier--format = 1 */
998 coverage; /* Offset to Coverage table--from
999 * beginning of subtable */
1000 ArrayOf<EntryExitRecord>
1001 entryExitRecord; /* Array of EntryExit records--in
1002 * Coverage Index order */
1004 ASSERT_SIZE (CursivePosFormat1, 6);
1008 friend struct PosLookupSubTable;
1011 inline bool apply (hb_apply_context_t *context) const
1015 case 1: return u.format1->apply (context);
1016 default:return false;
1020 inline bool sanitize (hb_sanitize_context_t *context) {
1022 if (!SANITIZE (u.format)) return false;
1024 case 1: return u.format1->sanitize (context);
1025 default:return true;
1031 USHORT format; /* Format identifier */
1032 CursivePosFormat1 format1[VAR];
1037 typedef AnchorMatrix BaseArray; /* base-major--
1038 * in order of BaseCoverage Index--,
1040 * ordered by class--zero-based. */
1042 struct MarkBasePosFormat1
1044 friend struct MarkBasePos;
1047 inline bool apply (hb_apply_context_t *context) const
1050 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1051 if (likely (mark_index == NOT_COVERED))
1054 /* now we search backwards for a non-mark glyph */
1055 unsigned int property;
1056 unsigned int j = context->buffer->in_pos;
1062 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1065 /* The following assertion is too strong. */
1066 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1070 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1071 if (base_index == NOT_COVERED)
1074 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1077 inline bool sanitize (hb_sanitize_context_t *context) {
1079 return SANITIZE_SELF ()
1080 && SANITIZE_WITH_BASE (this, markCoverage)
1081 && SANITIZE_WITH_BASE (this, baseCoverage)
1082 && SANITIZE_WITH_BASE (this, markArray)
1083 && likely (baseArray.sanitize (context, CharP(this), (unsigned int) classCount));
1087 USHORT format; /* Format identifier--format = 1 */
1089 markCoverage; /* Offset to MarkCoverage table--from
1090 * beginning of MarkBasePos subtable */
1092 baseCoverage; /* Offset to BaseCoverage table--from
1093 * beginning of MarkBasePos subtable */
1094 USHORT classCount; /* Number of classes defined for marks */
1096 markArray; /* Offset to MarkArray table--from
1097 * beginning of MarkBasePos subtable */
1099 baseArray; /* Offset to BaseArray table--from
1100 * beginning of MarkBasePos subtable */
1102 ASSERT_SIZE (MarkBasePosFormat1, 12);
1106 friend struct PosLookupSubTable;
1109 inline bool apply (hb_apply_context_t *context) const
1113 case 1: return u.format1->apply (context);
1114 default:return false;
1118 inline bool sanitize (hb_sanitize_context_t *context) {
1120 if (!SANITIZE (u.format)) return false;
1122 case 1: return u.format1->sanitize (context);
1123 default:return true;
1129 USHORT format; /* Format identifier */
1130 MarkBasePosFormat1 format1[VAR];
1135 typedef AnchorMatrix LigatureAttach; /* component-major--
1136 * in order of writing direction--,
1138 * ordered by class--zero-based. */
1140 typedef OffsetListOf<LigatureAttach> LigatureArray;
1141 /* Array of LigatureAttach
1143 * LigatureCoverage Index */
1145 struct MarkLigPosFormat1
1147 friend struct MarkLigPos;
1150 inline bool apply (hb_apply_context_t *context) const
1153 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1154 if (likely (mark_index == NOT_COVERED))
1157 /* now we search backwards for a non-mark glyph */
1158 unsigned int property;
1159 unsigned int j = context->buffer->in_pos;
1165 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1168 /* The following assertion is too strong. */
1169 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1173 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1174 if (lig_index == NOT_COVERED)
1177 const LigatureArray& lig_array = this+ligatureArray;
1178 const LigatureAttach& lig_attach = lig_array[lig_index];
1180 /* Find component to attach to */
1181 unsigned int comp_count = lig_attach.rows;
1182 if (unlikely (!comp_count))
1184 unsigned int comp_index;
1185 /* We must now check whether the ligature ID of the current mark glyph
1186 * is identical to the ligature ID of the found ligature. If yes, we
1187 * can directly use the component index. If not, we attach the mark
1188 * glyph to the last component of the ligature. */
1189 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1191 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1192 if (comp_index >= comp_count)
1193 comp_index = comp_count - 1;
1196 comp_index = comp_count - 1;
1198 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1201 inline bool sanitize (hb_sanitize_context_t *context) {
1203 return SANITIZE_SELF ()
1204 && SANITIZE_WITH_BASE (this, markCoverage)
1205 && SANITIZE_WITH_BASE (this, ligatureCoverage)
1206 && SANITIZE_WITH_BASE (this, markArray)
1207 && likely (ligatureArray.sanitize (context, CharP(this), (unsigned int) classCount));
1211 USHORT format; /* Format identifier--format = 1 */
1213 markCoverage; /* Offset to Mark Coverage table--from
1214 * beginning of MarkLigPos subtable */
1216 ligatureCoverage; /* Offset to Ligature Coverage
1217 * table--from beginning of MarkLigPos
1219 USHORT classCount; /* Number of defined mark classes */
1221 markArray; /* Offset to MarkArray table--from
1222 * beginning of MarkLigPos subtable */
1223 OffsetTo<LigatureArray>
1224 ligatureArray; /* Offset to LigatureArray table--from
1225 * beginning of MarkLigPos subtable */
1227 ASSERT_SIZE (MarkLigPosFormat1, 12);
1231 friend struct PosLookupSubTable;
1234 inline bool apply (hb_apply_context_t *context) const
1238 case 1: return u.format1->apply (context);
1239 default:return false;
1243 inline bool sanitize (hb_sanitize_context_t *context) {
1245 if (!SANITIZE (u.format)) return false;
1247 case 1: return u.format1->sanitize (context);
1248 default:return true;
1254 USHORT format; /* Format identifier */
1255 MarkLigPosFormat1 format1[VAR];
1260 typedef AnchorMatrix Mark2Array; /* mark2-major--
1261 * in order of Mark2Coverage Index--,
1263 * ordered by class--zero-based. */
1265 struct MarkMarkPosFormat1
1267 friend struct MarkMarkPos;
1270 inline bool apply (hb_apply_context_t *context) const
1273 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1274 if (likely (mark1_index == NOT_COVERED))
1277 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1278 unsigned int property;
1279 unsigned int j = context->buffer->in_pos;
1285 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1287 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1290 /* Two marks match only if they belong to the same base, or same component
1291 * of the same ligature. That is, the component numbers must match, and
1292 * if those are non-zero, the ligid number should also match. */
1293 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1294 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1297 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1298 if (mark2_index == NOT_COVERED)
1301 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1304 inline bool sanitize (hb_sanitize_context_t *context) {
1306 return SANITIZE_SELF ()
1307 && SANITIZE_WITH_BASE (this, mark1Coverage)
1308 && SANITIZE_WITH_BASE (this, mark2Coverage)
1309 && SANITIZE_WITH_BASE (this, mark1Array)
1310 && likely (mark2Array.sanitize (context, CharP(this), (unsigned int) classCount));
1314 USHORT format; /* Format identifier--format = 1 */
1316 mark1Coverage; /* Offset to Combining Mark1 Coverage
1317 * table--from beginning of MarkMarkPos
1320 mark2Coverage; /* Offset to Combining Mark2 Coverage
1321 * table--from beginning of MarkMarkPos
1323 USHORT classCount; /* Number of defined mark classes */
1325 mark1Array; /* Offset to Mark1Array table--from
1326 * beginning of MarkMarkPos subtable */
1327 OffsetTo<Mark2Array>
1328 mark2Array; /* Offset to Mark2Array table--from
1329 * beginning of MarkMarkPos subtable */
1331 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1335 friend struct PosLookupSubTable;
1338 inline bool apply (hb_apply_context_t *context) const
1342 case 1: return u.format1->apply (context);
1343 default:return false;
1347 inline bool sanitize (hb_sanitize_context_t *context) {
1349 if (!SANITIZE (u.format)) return false;
1351 case 1: return u.format1->sanitize (context);
1352 default:return true;
1358 USHORT format; /* Format identifier */
1359 MarkMarkPosFormat1 format1[VAR];
1364 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1366 struct ContextPos : Context
1368 friend struct PosLookupSubTable;
1371 inline bool apply (hb_apply_context_t *context) const
1374 return Context::apply (context, position_lookup);
1378 struct ChainContextPos : ChainContext
1380 friend struct PosLookupSubTable;
1383 inline bool apply (hb_apply_context_t *context) const
1386 return ChainContext::apply (context, position_lookup);
1391 struct ExtensionPos : Extension
1393 friend struct PosLookupSubTable;
1396 inline const struct PosLookupSubTable& get_subtable (void) const
1398 unsigned int offset = get_offset ();
1399 if (unlikely (!offset)) return Null(PosLookupSubTable);
1400 return StructAtOffset<PosLookupSubTable> (*this, offset);
1403 inline bool apply (hb_apply_context_t *context) const;
1405 inline bool sanitize (hb_sanitize_context_t *context);
1415 struct PosLookupSubTable
1417 friend struct PosLookup;
1431 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1434 switch (lookup_type) {
1435 case Single: return u.single->apply (context);
1436 case Pair: return u.pair->apply (context);
1437 case Cursive: return u.cursive->apply (context);
1438 case MarkBase: return u.markBase->apply (context);
1439 case MarkLig: return u.markLig->apply (context);
1440 case MarkMark: return u.markMark->apply (context);
1441 case Context: return u.context->apply (context);
1442 case ChainContext: return u.chainContext->apply (context);
1443 case Extension: return u.extension->apply (context);
1444 default:return false;
1448 inline bool sanitize (hb_sanitize_context_t *context) {
1450 if (!SANITIZE (u.format)) return false;
1452 case Single: return u.single->sanitize (context);
1453 case Pair: return u.pair->sanitize (context);
1454 case Cursive: return u.cursive->sanitize (context);
1455 case MarkBase: return u.markBase->sanitize (context);
1456 case MarkLig: return u.markLig->sanitize (context);
1457 case MarkMark: return u.markMark->sanitize (context);
1458 case Context: return u.context->sanitize (context);
1459 case ChainContext: return u.chainContext->sanitize (context);
1460 case Extension: return u.extension->sanitize (context);
1461 default:return true;
1468 SinglePos single[VAR];
1470 CursivePos cursive[VAR];
1471 MarkBasePos markBase[VAR];
1472 MarkLigPos markLig[VAR];
1473 MarkMarkPos markMark[VAR];
1474 ContextPos context[VAR];
1475 ChainContextPos chainContext[VAR];
1476 ExtensionPos extension[VAR];
1481 struct PosLookup : Lookup
1483 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1484 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1486 inline bool apply_once (hb_ot_layout_context_t *layout,
1487 hb_buffer_t *buffer,
1488 unsigned int context_length,
1489 unsigned int nesting_level_left) const
1491 unsigned int lookup_type = get_type ();
1492 hb_apply_context_t context[1] = {{0}};
1494 context->layout = layout;
1495 context->buffer = buffer;
1496 context->context_length = context_length;
1497 context->nesting_level_left = nesting_level_left;
1498 context->lookup_flag = get_flag ();
1500 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1503 for (unsigned int i = 0; i < get_subtable_count (); i++)
1504 if (get_subtable (i).apply (context, lookup_type))
1510 inline bool apply_string (hb_ot_layout_context_t *layout,
1511 hb_buffer_t *buffer,
1512 hb_mask_t mask) const
1515 #define BUFFER buffer
1518 if (unlikely (!buffer->in_length))
1521 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1524 while (buffer->in_pos < buffer->in_length)
1527 if (~IN_MASK (buffer->in_pos) & mask)
1529 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1535 /* Contrary to properties defined in GDEF, user-defined properties
1536 will always stop a possible cursive positioning. */
1537 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1547 inline bool sanitize (hb_sanitize_context_t *context) {
1549 if (unlikely (!Lookup::sanitize (context))) return false;
1550 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1551 return SANITIZE_WITH_BASE (this, list);
1555 typedef OffsetListOf<PosLookup> PosLookupList;
1556 ASSERT_SIZE (PosLookupList, 2);
1562 struct GPOS : GSUBGPOS
1564 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1566 inline const PosLookup& get_lookup (unsigned int i) const
1567 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1569 inline bool position_lookup (hb_ot_layout_context_t *layout,
1570 hb_buffer_t *buffer,
1571 unsigned int lookup_index,
1572 hb_mask_t mask) const
1573 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1575 inline bool sanitize (hb_sanitize_context_t *context) {
1577 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1578 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1579 return SANITIZE_WITH_BASE (this, list);
1582 ASSERT_SIZE (GPOS, 10);
1585 /* Out-of-class implementation for methods recursing */
1587 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1590 return get_subtable ().apply (context, get_type ());
1593 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1596 if (unlikely (!Extension::sanitize (context))) return false;
1597 unsigned int offset = get_offset ();
1598 if (unlikely (!offset)) return true;
1599 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1602 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1604 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1605 const PosLookup &l = gpos.get_lookup (lookup_index);
1607 if (unlikely (context->nesting_level_left == 0))
1610 if (unlikely (context->context_length < 1))
1613 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1617 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */