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"
32 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
34 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
38 typedef Value ValueRecord[VAR0];
39 ASSERT_SIZE_VAR (ValueRecord, 0, Value);
41 struct ValueFormat : USHORT
45 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
46 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
47 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
48 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
49 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
50 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
51 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
52 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
53 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
54 reserved = 0xF000, /* For future use */
56 devices = 0x00F0 /* Mask for having any Device table */
59 /* All fields are options. Only those available advance the value pointer. */
61 SHORT xPlacement; /* Horizontal adjustment for
62 * placement--in design units */
63 SHORT yPlacement; /* Vertical adjustment for
64 * placement--in design units */
65 SHORT xAdvance; /* Horizontal adjustment for
66 * advance--in design units (only used
67 * for horizontal writing) */
68 SHORT yAdvance; /* Vertical adjustment for advance--in
69 * design units (only used for vertical
71 Offset xPlaDevice; /* Offset to Device table for
72 * horizontal placement--measured from
73 * beginning of PosTable (may be NULL) */
74 Offset yPlaDevice; /* Offset to Device table for vertical
75 * placement--measured from beginning
76 * of PosTable (may be NULL) */
77 Offset xAdvDevice; /* Offset to Device table for
78 * horizontal advance--measured from
79 * beginning of PosTable (may be NULL) */
80 Offset yAdvDevice; /* Offset to Device table for vertical
81 * advance--measured from beginning of
82 * PosTable (may be NULL) */
85 inline unsigned int get_len () const
86 { return _hb_popcount32 ((unsigned int) *this); }
87 inline unsigned int get_size () const
88 { return get_len () * Value::get_size (); }
90 void apply_value (hb_ot_layout_context_t *layout_context,
93 hb_internal_glyph_position_t *glyph_pos) const
95 unsigned int x_ppem, y_ppem;
96 hb_16dot16_t x_scale, y_scale;
97 unsigned int format = *this;
101 x_scale = layout_context->font->x_scale;
102 y_scale = layout_context->font->y_scale;
103 /* design units -> fractional pixel */
104 if (format & xPlacement) glyph_pos->x_offset += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
105 if (format & yPlacement) glyph_pos->y_offset += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
106 if (format & xAdvance) glyph_pos->x_advance += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
107 if (format & yAdvance) glyph_pos->y_advance += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
109 x_ppem = layout_context->font->x_ppem;
110 y_ppem = layout_context->font->y_ppem;
111 /* pixel -> fractional pixel */
112 if (format & xPlaDevice) {
113 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
115 if (format & yPlaDevice) {
116 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
118 if (format & xAdvDevice) {
119 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
121 if (format & yAdvDevice) {
122 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
127 inline bool sanitize_value_devices (SANITIZE_ARG_DEF, void *base, const Value *values) {
128 unsigned int format = *this;
130 if (format & xPlacement) values++;
131 if (format & yPlacement) values++;
132 if (format & xAdvance) values++;
133 if (format & yAdvance) values++;
135 if ((format & xPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
136 if ((format & yPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
137 if ((format & xAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
138 if ((format & yAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
145 inline bool has_device () {
146 unsigned int format = *this;
147 return (format & devices) != 0;
150 inline bool sanitize_value (SANITIZE_ARG_DEF, void *base, const Value *values) {
153 return SANITIZE_MEM (values, get_size ()) &&
154 (!has_device () || sanitize_value_devices (SANITIZE_ARG, base, values));
157 inline bool sanitize_values (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count) {
159 unsigned int len = get_len ();
161 if (!SANITIZE_ARRAY (values, get_size (), count)) return false;
163 if (!has_device ()) return true;
165 for (unsigned int i = 0; i < count; i++) {
166 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
174 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
175 inline bool sanitize_values_stride_unsafe (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count, unsigned int stride) {
178 if (!has_device ()) return true;
180 for (unsigned int i = 0; i < count; i++) {
181 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
189 ASSERT_SIZE (ValueFormat, 2);
194 friend struct Anchor;
197 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
198 hb_position_t *x, hb_position_t *y) const
200 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
201 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
204 inline bool sanitize (SANITIZE_ARG_DEF) {
206 return SANITIZE_SELF ();
210 USHORT format; /* Format identifier--format = 1 */
211 SHORT xCoordinate; /* Horizontal value--in design units */
212 SHORT yCoordinate; /* Vertical value--in design units */
214 ASSERT_SIZE (AnchorFormat1, 6);
218 friend struct Anchor;
221 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
222 hb_position_t *x, hb_position_t *y) const
224 unsigned int x_ppem = layout_context->font->x_ppem;
225 unsigned int y_ppem = layout_context->font->y_ppem;
226 hb_position_t cx, cy;
229 if (x_ppem || y_ppem)
230 ret = hb_font_get_contour_point (layout_context->font, layout_context->face, anchorPoint, glyph_id, &cx, &cy);
231 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
232 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
235 inline bool sanitize (SANITIZE_ARG_DEF) {
237 return SANITIZE_SELF ();
241 USHORT format; /* Format identifier--format = 2 */
242 SHORT xCoordinate; /* Horizontal value--in design units */
243 SHORT yCoordinate; /* Vertical value--in design units */
244 USHORT anchorPoint; /* Index to glyph contour point */
246 ASSERT_SIZE (AnchorFormat2, 8);
250 friend struct Anchor;
253 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
254 hb_position_t *x, hb_position_t *y) const
256 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
257 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
259 /* pixel -> fractional pixel */
260 if (layout_context->font->x_ppem)
261 *x += (this+xDeviceTable).get_delta (layout_context->font->x_ppem) << 16;
262 if (layout_context->font->y_ppem)
263 *y += (this+yDeviceTable).get_delta (layout_context->font->y_ppem) << 16;
266 inline bool sanitize (SANITIZE_ARG_DEF) {
268 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
272 USHORT format; /* Format identifier--format = 3 */
273 SHORT xCoordinate; /* Horizontal value--in design units */
274 SHORT yCoordinate; /* Vertical value--in design units */
276 xDeviceTable; /* Offset to Device table for X
277 * coordinate-- from beginning of
278 * Anchor table (may be NULL) */
280 yDeviceTable; /* Offset to Device table for Y
281 * coordinate-- from beginning of
282 * Anchor table (may be NULL) */
284 ASSERT_SIZE (AnchorFormat3, 10);
288 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
289 hb_position_t *x, hb_position_t *y) const
293 case 1: u.format1->get_anchor (layout_context, glyph_id, x, y); return;
294 case 2: u.format2->get_anchor (layout_context, glyph_id, x, y); return;
295 case 3: u.format3->get_anchor (layout_context, glyph_id, x, y); return;
300 inline bool sanitize (SANITIZE_ARG_DEF) {
302 if (!SANITIZE (u.format)) return false;
304 case 1: return u.format1->sanitize (SANITIZE_ARG);
305 case 2: return u.format2->sanitize (SANITIZE_ARG);
306 case 3: return u.format3->sanitize (SANITIZE_ARG);
313 USHORT format; /* Format identifier */
314 AnchorFormat1 format1[VAR];
315 AnchorFormat2 format2[VAR];
316 AnchorFormat3 format3[VAR];
323 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
324 if (HB_UNLIKELY (row >= rows || col >= cols)) return Null(Anchor);
325 return this+matrix[row * cols + col];
328 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
330 if (!SANITIZE_SELF ()) return false;
331 if (HB_UNLIKELY (cols >= ((unsigned int) -1) / rows)) return false;
332 unsigned int count = rows * cols;
333 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
334 for (unsigned int i = 0; i < count; i++)
335 if (!SANITIZE_THIS (matrix[i])) return false;
339 USHORT rows; /* Number of rows */
342 matrix[VAR]; /* Matrix of offsets to Anchor tables--
343 * from beginning of AnchorMatrix table */
345 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
350 friend struct MarkArray;
352 static inline unsigned int get_size () { return sizeof (MarkRecord); }
354 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
356 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
360 USHORT klass; /* Class defined for this mark */
362 markAnchor; /* Offset to Anchor table--from
363 * beginning of MarkArray table */
365 ASSERT_SIZE (MarkRecord, 4);
369 inline bool apply (APPLY_ARG_DEF,
370 unsigned int mark_index, unsigned int glyph_index,
371 const AnchorMatrix &anchors, unsigned int class_count,
372 unsigned int glyph_pos) const
375 const MarkRecord &record = markRecord[mark_index];
376 unsigned int mark_class = record.klass;
378 const Anchor& mark_anchor = this + record.markAnchor;
379 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
381 hb_position_t mark_x, mark_y, base_x, base_y;
383 mark_anchor.get_anchor (layout_context, IN_CURGLYPH (), &mark_x, &mark_y);
384 glyph_anchor.get_anchor (layout_context, IN_GLYPH (glyph_pos), &base_x, &base_y);
386 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
389 o->x_offset = base_x - mark_x;
390 o->y_offset = base_y - mark_y;
391 o->back = buffer->in_pos - glyph_pos;
397 inline bool sanitize (SANITIZE_ARG_DEF) {
399 return SANITIZE_THIS (markRecord);
404 markRecord; /* Array of MarkRecords--in Coverage order */
406 ASSERT_SIZE (MarkArray, 2);
411 struct SinglePosFormat1
413 friend struct SinglePos;
416 inline bool apply (APPLY_ARG_DEF) const
419 unsigned int index = (this+coverage) (IN_CURGLYPH ());
420 if (HB_LIKELY (index == NOT_COVERED))
423 valueFormat.apply_value (layout_context, CharP(this), values, CURPOSITION ());
429 inline bool sanitize (SANITIZE_ARG_DEF) {
431 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
432 valueFormat.sanitize_value (SANITIZE_ARG, CharP(this), values);
436 USHORT format; /* Format identifier--format = 1 */
438 coverage; /* Offset to Coverage table--from
439 * beginning of subtable */
440 ValueFormat valueFormat; /* Defines the types of data in the
442 ValueRecord values; /* Defines positioning
443 * value(s)--applied to all glyphs in
444 * the Coverage table */
446 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
448 struct SinglePosFormat2
450 friend struct SinglePos;
453 inline bool apply (APPLY_ARG_DEF) const
456 unsigned int index = (this+coverage) (IN_CURGLYPH ());
457 if (HB_LIKELY (index == NOT_COVERED))
460 if (HB_LIKELY (index >= valueCount))
463 valueFormat.apply_value (layout_context, CharP(this),
464 &values[index * valueFormat.get_len ()],
471 inline bool sanitize (SANITIZE_ARG_DEF) {
473 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
474 valueFormat.sanitize_values (SANITIZE_ARG, CharP(this), values, valueCount);
478 USHORT format; /* Format identifier--format = 2 */
480 coverage; /* Offset to Coverage table--from
481 * beginning of subtable */
482 ValueFormat valueFormat; /* Defines the types of data in the
484 USHORT valueCount; /* Number of ValueRecords */
485 ValueRecord values; /* Array of ValueRecords--positioning
486 * values applied to glyphs */
488 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
492 friend struct PosLookupSubTable;
495 inline bool apply (APPLY_ARG_DEF) const
499 case 1: return u.format1->apply (APPLY_ARG);
500 case 2: return u.format2->apply (APPLY_ARG);
501 default:return false;
505 inline bool sanitize (SANITIZE_ARG_DEF) {
507 if (!SANITIZE (u.format)) return false;
509 case 1: return u.format1->sanitize (SANITIZE_ARG);
510 case 2: return u.format2->sanitize (SANITIZE_ARG);
517 USHORT format; /* Format identifier */
518 SinglePosFormat1 format1[VAR];
519 SinglePosFormat2 format2[VAR];
524 struct PairValueRecord
526 friend struct PairPosFormat1;
529 GlyphID secondGlyph; /* GlyphID of second glyph in the
530 * pair--first glyph is listed in the
532 ValueRecord values; /* Positioning data for the first glyph
533 * followed by for second glyph */
535 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
539 friend struct PairPosFormat1;
541 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
542 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
544 if (!SANITIZE_SELF ()) return false;
545 unsigned int count = (1 + format_len) * len;
546 return SANITIZE_ARRAY (array, USHORT::get_size (), count);
550 USHORT len; /* Number of PairValueRecords */
552 array[VAR]; /* Array of PairValueRecords--ordered
553 * by GlyphID of the second glyph */
555 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
557 struct PairPosFormat1
559 friend struct PairPos;
562 inline bool apply (APPLY_ARG_DEF) const
565 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
566 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
569 unsigned int index = (this+coverage) (IN_CURGLYPH ());
570 if (HB_LIKELY (index == NOT_COVERED))
573 unsigned int j = buffer->in_pos + 1;
574 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
576 if (HB_UNLIKELY (j == end))
581 unsigned int len1 = valueFormat1.get_len ();
582 unsigned int len2 = valueFormat2.get_len ();
583 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
585 const PairSet &pair_set = this+pairSet[index];
586 unsigned int count = pair_set.len;
587 const PairValueRecord *record = pair_set.array;
588 for (unsigned int i = 0; i < count; i++)
590 if (IN_GLYPH (j) == record->secondGlyph)
592 valueFormat1.apply_value (layout_context, CharP(this), &record->values[0], CURPOSITION ());
593 valueFormat2.apply_value (layout_context, CharP(this), &record->values[len1], POSITION (j));
599 record = &StructAtOffset<PairValueRecord> (*record, record_size);
605 inline bool sanitize (SANITIZE_ARG_DEF) {
608 unsigned int len1 = valueFormat1.get_len ();
609 unsigned int len2 = valueFormat2.get_len ();
611 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
612 HB_LIKELY (pairSet.sanitize (SANITIZE_ARG, CharP(this), len1 + len2)))) return false;
614 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
616 unsigned int stride = 1 + len1 + len2;
617 unsigned int count1 = pairSet.len;
618 for (unsigned int i = 0; i < count1; i++)
620 const PairSet &pair_set = this+pairSet[i];
622 unsigned int count2 = pair_set.len;
623 const PairValueRecord *record = pair_set.array;
624 if (!(valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[0], count2, stride) &&
625 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[len1], count2, stride)))
633 USHORT format; /* Format identifier--format = 1 */
635 coverage; /* Offset to Coverage table--from
636 * beginning of subtable */
637 ValueFormat valueFormat1; /* Defines the types of data in
638 * ValueRecord1--for the first glyph
639 * in the pair--may be zero (0) */
640 ValueFormat valueFormat2; /* Defines the types of data in
641 * ValueRecord2--for the second glyph
642 * in the pair--may be zero (0) */
643 OffsetArrayOf<PairSet>
644 pairSet; /* Array of PairSet tables
645 * ordered by Coverage Index */
647 ASSERT_SIZE (PairPosFormat1, 10);
649 struct PairPosFormat2
651 friend struct PairPos;
654 inline bool apply (APPLY_ARG_DEF) const
657 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
658 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
661 unsigned int index = (this+coverage) (IN_CURGLYPH ());
662 if (HB_LIKELY (index == NOT_COVERED))
665 unsigned int j = buffer->in_pos + 1;
666 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
668 if (HB_UNLIKELY (j == end))
673 unsigned int len1 = valueFormat1.get_len ();
674 unsigned int len2 = valueFormat2.get_len ();
675 unsigned int record_len = len1 + len2;
677 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
678 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
679 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
682 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
683 valueFormat1.apply_value (layout_context, CharP(this), v, CURPOSITION ());
684 valueFormat2.apply_value (layout_context, CharP(this), v + len1, POSITION (j));
693 inline bool sanitize (SANITIZE_ARG_DEF) {
695 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
696 SANITIZE_THIS2 (classDef1, classDef2))) return false;
698 unsigned int len1 = valueFormat1.get_len ();
699 unsigned int len2 = valueFormat2.get_len ();
700 unsigned int stride = len1 + len2;
701 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
702 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
703 return SANITIZE_ARRAY (values, record_size, count) &&
704 valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[0], count, stride) &&
705 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[len1], count, stride);
709 USHORT format; /* Format identifier--format = 2 */
711 coverage; /* Offset to Coverage table--from
712 * beginning of subtable */
713 ValueFormat valueFormat1; /* ValueRecord definition--for the
714 * first glyph of the pair--may be zero
716 ValueFormat valueFormat2; /* ValueRecord definition--for the
717 * second glyph of the pair--may be
720 classDef1; /* Offset to ClassDef table--from
721 * beginning of PairPos subtable--for
722 * the first glyph of the pair */
724 classDef2; /* Offset to ClassDef table--from
725 * beginning of PairPos subtable--for
726 * the second glyph of the pair */
727 USHORT class1Count; /* Number of classes in ClassDef1
728 * table--includes Class0 */
729 USHORT class2Count; /* Number of classes in ClassDef2
730 * table--includes Class0 */
731 ValueRecord values; /* Matrix of value pairs:
732 * class1-major, class2-minor,
733 * Each entry has value1 and value2 */
735 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
739 friend struct PosLookupSubTable;
742 inline bool apply (APPLY_ARG_DEF) const
746 case 1: return u.format1->apply (APPLY_ARG);
747 case 2: return u.format2->apply (APPLY_ARG);
748 default:return false;
752 inline bool sanitize (SANITIZE_ARG_DEF) {
754 if (!SANITIZE (u.format)) return false;
756 case 1: return u.format1->sanitize (SANITIZE_ARG);
757 case 2: return u.format2->sanitize (SANITIZE_ARG);
764 USHORT format; /* Format identifier */
765 PairPosFormat1 format1[VAR];
766 PairPosFormat2 format2[VAR];
771 struct EntryExitRecord
773 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
775 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
777 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
781 entryAnchor; /* Offset to EntryAnchor table--from
782 * beginning of CursivePos
783 * subtable--may be NULL */
785 exitAnchor; /* Offset to ExitAnchor table--from
786 * beginning of CursivePos
787 * subtable--may be NULL */
789 ASSERT_SIZE (EntryExitRecord, 4);
791 struct CursivePosFormat1
793 friend struct CursivePos;
796 inline bool apply (APPLY_ARG_DEF) const
799 /* Now comes the messiest part of the whole OpenType
800 specification. At first glance, cursive connections seem easy
801 to understand, but there are pitfalls! The reason is that
802 the specs don't mention how to compute the advance values
803 resp. glyph offsets. I was told it would be an omission, to
804 be fixed in the next OpenType version... Again many thanks to
805 Andrei Burago <andreib@microsoft.com> for clarifications.
807 Consider the following example:
820 glyph1: advance width = 12
823 glyph2: advance width = 11
826 LSB is 1 for both glyphs (so the boxes drawn above are glyph
827 bboxes). Writing direction is R2L; `0' denotes the glyph's
830 Now the surprising part: The advance width of the *left* glyph
831 (resp. of the *bottom* glyph) will be modified, no matter
832 whether the writing direction is L2R or R2L (resp. T2B or
833 B2T)! This assymetry is caused by the fact that the glyph's
834 coordinate origin is always the lower left corner for all
837 Continuing the above example, we can compute the new
838 (horizontal) advance width of glyph2 as
842 and the new vertical offset of glyph2 as
847 Vertical writing direction is far more complicated:
849 a) Assuming that we recompute the advance height of the lower glyph:
856 yadv2 | 0+--+------+ -- BSB1 --
859 BSB2 -- 0+--------+ --
862 glyph1: advance height = 6
865 glyph2: advance height = 7
868 TSB is 1 for both glyphs; writing direction is T2B.
871 BSB1 = yadv1 - (TSB1 + ymax1)
872 BSB2 = yadv2 - (TSB2 + ymax2)
875 vertical advance width of glyph2
876 = y_offset + BSB2 - BSB1
877 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
878 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
879 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
882 b) Assuming that we recompute the advance height of the upper glyph:
887 TSB2 -- +-----+--+ 1 | yadv1 ymax1
889 yadv2 | 0+--+------+ -- --
890 ymax2 | 2 | -- y_offset
895 glyph1: advance height = 6
898 glyph2: advance height = 7
901 TSB is 1 for both glyphs; writing direction is T2B.
905 vertical advance width of glyph2
906 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
907 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
910 Comparing a) with b) shows that b) is easier to compute. I'll wait
911 for a reply from Andrei to see what should really be implemented...
913 Since horizontal advance widths or vertical advance heights
914 can be used alone but not together, no ambiguity occurs. */
916 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &layout_context->info.gpos;
917 hb_codepoint_t last_pos = gpi->last;
918 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
920 /* We don't handle mark glyphs here. */
921 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
924 unsigned int index = (this+coverage) (IN_CURGLYPH ());
925 if (HB_LIKELY (index == NOT_COVERED))
928 const EntryExitRecord &record = entryExitRecord[index];
930 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
933 hb_position_t entry_x, entry_y;
934 (this+record.entryAnchor).get_anchor (layout_context, IN_CURGLYPH (), &entry_x, &entry_y);
938 if (buffer->direction == HB_DIRECTION_RTL)
940 /* advance is absolute, not relative */
941 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
945 /* advance is absolute, not relative */
946 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
949 if (context->lookup_flag & LookupFlag::RightToLeft)
951 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
952 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
956 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
957 POSITION (buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
961 if (record.exitAnchor)
963 gpi->last = buffer->in_pos;
964 (this+record.exitAnchor).get_anchor (layout_context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
971 inline bool sanitize (SANITIZE_ARG_DEF) {
973 return SANITIZE_THIS2 (coverage, entryExitRecord);
977 USHORT format; /* Format identifier--format = 1 */
979 coverage; /* Offset to Coverage table--from
980 * beginning of subtable */
981 ArrayOf<EntryExitRecord>
982 entryExitRecord; /* Array of EntryExit records--in
983 * Coverage Index order */
985 ASSERT_SIZE (CursivePosFormat1, 6);
989 friend struct PosLookupSubTable;
992 inline bool apply (APPLY_ARG_DEF) const
996 case 1: return u.format1->apply (APPLY_ARG);
997 default:return false;
1001 inline bool sanitize (SANITIZE_ARG_DEF) {
1003 if (!SANITIZE (u.format)) return false;
1005 case 1: return u.format1->sanitize (SANITIZE_ARG);
1006 default:return true;
1012 USHORT format; /* Format identifier */
1013 CursivePosFormat1 format1[VAR];
1018 typedef AnchorMatrix BaseArray; /* base-major--
1019 * in order of BaseCoverage Index--,
1021 * ordered by class--zero-based. */
1023 struct MarkBasePosFormat1
1025 friend struct MarkBasePos;
1028 inline bool apply (APPLY_ARG_DEF) const
1031 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1032 if (HB_LIKELY (mark_index == NOT_COVERED))
1035 /* now we search backwards for a non-mark glyph */
1036 unsigned int property;
1037 unsigned int j = buffer->in_pos;
1040 if (HB_UNLIKELY (!j))
1043 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1046 /* The following assertion is too strong. */
1047 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1051 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1052 if (base_index == NOT_COVERED)
1055 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
1058 inline bool sanitize (SANITIZE_ARG_DEF) {
1060 return SANITIZE_SELF () && SANITIZE_THIS3 (markCoverage, baseCoverage, markArray) &&
1061 HB_LIKELY (baseArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1065 USHORT format; /* Format identifier--format = 1 */
1067 markCoverage; /* Offset to MarkCoverage table--from
1068 * beginning of MarkBasePos subtable */
1070 baseCoverage; /* Offset to BaseCoverage table--from
1071 * beginning of MarkBasePos subtable */
1072 USHORT classCount; /* Number of classes defined for marks */
1074 markArray; /* Offset to MarkArray table--from
1075 * beginning of MarkBasePos subtable */
1077 baseArray; /* Offset to BaseArray table--from
1078 * beginning of MarkBasePos subtable */
1080 ASSERT_SIZE (MarkBasePosFormat1, 12);
1084 friend struct PosLookupSubTable;
1087 inline bool apply (APPLY_ARG_DEF) const
1091 case 1: return u.format1->apply (APPLY_ARG);
1092 default:return false;
1096 inline bool sanitize (SANITIZE_ARG_DEF) {
1098 if (!SANITIZE (u.format)) return false;
1100 case 1: return u.format1->sanitize (SANITIZE_ARG);
1101 default:return true;
1107 USHORT format; /* Format identifier */
1108 MarkBasePosFormat1 format1[VAR];
1113 typedef AnchorMatrix LigatureAttach; /* component-major--
1114 * in order of writing direction--,
1116 * ordered by class--zero-based. */
1118 typedef OffsetListOf<LigatureAttach> LigatureArray;
1119 /* Array of LigatureAttach
1121 * LigatureCoverage Index */
1123 struct MarkLigPosFormat1
1125 friend struct MarkLigPos;
1128 inline bool apply (APPLY_ARG_DEF) const
1131 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1132 if (HB_LIKELY (mark_index == NOT_COVERED))
1135 /* now we search backwards for a non-mark glyph */
1136 unsigned int property;
1137 unsigned int j = buffer->in_pos;
1140 if (HB_UNLIKELY (!j))
1143 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1146 /* The following assertion is too strong. */
1147 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1151 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1152 if (lig_index == NOT_COVERED)
1155 const LigatureArray& lig_array = this+ligatureArray;
1156 const LigatureAttach& lig_attach = lig_array[lig_index];
1158 /* Find component to attach to */
1159 unsigned int comp_count = lig_attach.rows;
1160 if (HB_UNLIKELY (!comp_count))
1162 unsigned int comp_index;
1163 /* We must now check whether the ligature ID of the current mark glyph
1164 * is identical to the ligature ID of the found ligature. If yes, we
1165 * can directly use the component index. If not, we attach the mark
1166 * glyph to the last component of the ligature. */
1167 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (buffer->in_pos) && IN_COMPONENT (buffer->in_pos))
1169 comp_index = IN_COMPONENT (buffer->in_pos) - 1;
1170 if (comp_index >= comp_count)
1171 comp_index = comp_count - 1;
1174 comp_index = comp_count - 1;
1176 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1179 inline bool sanitize (SANITIZE_ARG_DEF) {
1181 return SANITIZE_SELF () && SANITIZE_THIS3 (markCoverage, ligatureCoverage, markArray) &&
1182 HB_LIKELY (ligatureArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1186 USHORT format; /* Format identifier--format = 1 */
1188 markCoverage; /* Offset to Mark Coverage table--from
1189 * beginning of MarkLigPos subtable */
1191 ligatureCoverage; /* Offset to Ligature Coverage
1192 * table--from beginning of MarkLigPos
1194 USHORT classCount; /* Number of defined mark classes */
1196 markArray; /* Offset to MarkArray table--from
1197 * beginning of MarkLigPos subtable */
1198 OffsetTo<LigatureArray>
1199 ligatureArray; /* Offset to LigatureArray table--from
1200 * beginning of MarkLigPos subtable */
1202 ASSERT_SIZE (MarkLigPosFormat1, 12);
1206 friend struct PosLookupSubTable;
1209 inline bool apply (APPLY_ARG_DEF) const
1213 case 1: return u.format1->apply (APPLY_ARG);
1214 default:return false;
1218 inline bool sanitize (SANITIZE_ARG_DEF) {
1220 if (!SANITIZE (u.format)) return false;
1222 case 1: return u.format1->sanitize (SANITIZE_ARG);
1223 default:return true;
1229 USHORT format; /* Format identifier */
1230 MarkLigPosFormat1 format1[VAR];
1235 typedef AnchorMatrix Mark2Array; /* mark2-major--
1236 * in order of Mark2Coverage Index--,
1238 * ordered by class--zero-based. */
1240 struct MarkMarkPosFormat1
1242 friend struct MarkMarkPos;
1245 inline bool apply (APPLY_ARG_DEF) const
1248 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1249 if (HB_LIKELY (mark1_index == NOT_COVERED))
1252 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1253 unsigned int property;
1254 unsigned int j = buffer->in_pos;
1257 if (HB_UNLIKELY (!j))
1260 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, &property));
1262 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1265 /* Two marks match only if they belong to the same base, or same component
1266 * of the same ligature. That is, the component numbers must match, and
1267 * if those are non-zero, the ligid number should also match. */
1268 if ((IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos)) ||
1269 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (buffer->in_pos)))
1272 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1273 if (mark2_index == NOT_COVERED)
1276 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1279 inline bool sanitize (SANITIZE_ARG_DEF) {
1281 return SANITIZE_SELF () && SANITIZE_THIS3 (mark1Coverage, mark2Coverage, mark1Array) &&
1282 HB_LIKELY (mark2Array.sanitize (SANITIZE_ARG, CharP(this), classCount));
1286 USHORT format; /* Format identifier--format = 1 */
1288 mark1Coverage; /* Offset to Combining Mark1 Coverage
1289 * table--from beginning of MarkMarkPos
1292 mark2Coverage; /* Offset to Combining Mark2 Coverage
1293 * table--from beginning of MarkMarkPos
1295 USHORT classCount; /* Number of defined mark classes */
1297 mark1Array; /* Offset to Mark1Array table--from
1298 * beginning of MarkMarkPos subtable */
1299 OffsetTo<Mark2Array>
1300 mark2Array; /* Offset to Mark2Array table--from
1301 * beginning of MarkMarkPos subtable */
1303 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1307 friend struct PosLookupSubTable;
1310 inline bool apply (APPLY_ARG_DEF) const
1314 case 1: return u.format1->apply (APPLY_ARG);
1315 default:return false;
1319 inline bool sanitize (SANITIZE_ARG_DEF) {
1321 if (!SANITIZE (u.format)) return false;
1323 case 1: return u.format1->sanitize (SANITIZE_ARG);
1324 default:return true;
1330 USHORT format; /* Format identifier */
1331 MarkMarkPosFormat1 format1[VAR];
1336 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1338 struct ContextPos : Context
1340 friend struct PosLookupSubTable;
1343 inline bool apply (APPLY_ARG_DEF) const
1346 return Context::apply (APPLY_ARG, position_lookup);
1350 struct ChainContextPos : ChainContext
1352 friend struct PosLookupSubTable;
1355 inline bool apply (APPLY_ARG_DEF) const
1358 return ChainContext::apply (APPLY_ARG, position_lookup);
1363 struct ExtensionPos : Extension
1365 friend struct PosLookupSubTable;
1368 inline const struct PosLookupSubTable& get_subtable (void) const
1370 unsigned int offset = get_offset ();
1371 if (HB_UNLIKELY (!offset)) return Null(PosLookupSubTable);
1372 return StructAtOffset<PosLookupSubTable> (*this, offset);
1375 inline bool apply (APPLY_ARG_DEF) const;
1377 inline bool sanitize (SANITIZE_ARG_DEF);
1387 struct PosLookupSubTable
1389 friend struct PosLookup;
1403 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1406 switch (lookup_type) {
1407 case Single: return u.single->apply (APPLY_ARG);
1408 case Pair: return u.pair->apply (APPLY_ARG);
1409 case Cursive: return u.cursive->apply (APPLY_ARG);
1410 case MarkBase: return u.markBase->apply (APPLY_ARG);
1411 case MarkLig: return u.markLig->apply (APPLY_ARG);
1412 case MarkMark: return u.markMark->apply (APPLY_ARG);
1413 case Context: return u.context->apply (APPLY_ARG);
1414 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1415 case Extension: return u.extension->apply (APPLY_ARG);
1416 default:return false;
1420 inline bool sanitize (SANITIZE_ARG_DEF) {
1422 if (!SANITIZE (u.format)) return false;
1424 case Single: return u.single->sanitize (SANITIZE_ARG);
1425 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1426 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1427 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1428 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1429 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1430 case Context: return u.context->sanitize (SANITIZE_ARG);
1431 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1432 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1433 default:return true;
1440 SinglePos single[VAR];
1442 CursivePos cursive[VAR];
1443 MarkBasePos markBase[VAR];
1444 MarkLigPos markLig[VAR];
1445 MarkMarkPos markMark[VAR];
1446 ContextPos context[VAR];
1447 ChainContextPos chainContext[VAR];
1448 ExtensionPos extension[VAR];
1453 struct PosLookup : Lookup
1455 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1456 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1458 inline bool apply_once (hb_ot_layout_context_t *layout_context,
1459 hb_buffer_t *buffer,
1460 unsigned int context_length,
1461 unsigned int nesting_level_left,
1462 unsigned int apply_depth) const
1464 unsigned int lookup_type = get_type ();
1465 hb_apply_context_t context[1];
1467 context->nesting_level_left = nesting_level_left;
1468 context->lookup_flag = get_flag ();
1470 if (!_hb_ot_layout_check_glyph_property (layout_context->face, IN_CURINFO (), context->lookup_flag, &context->property))
1473 for (unsigned int i = 0; i < get_subtable_count (); i++)
1474 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
1480 inline bool apply_string (hb_ot_layout_context_t *layout_context,
1481 hb_buffer_t *buffer,
1482 hb_mask_t mask) const
1486 if (HB_UNLIKELY (!buffer->in_length))
1489 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1492 while (buffer->in_pos < buffer->in_length)
1495 if (~IN_MASK (buffer->in_pos) & mask)
1497 done = apply_once (layout_context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, 0);
1503 /* Contrary to properties defined in GDEF, user-defined properties
1504 will always stop a possible cursive positioning. */
1505 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1515 inline bool sanitize (SANITIZE_ARG_DEF) {
1517 if (HB_UNLIKELY (!Lookup::sanitize (SANITIZE_ARG))) return false;
1518 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1519 return SANITIZE_THIS (list);
1523 typedef OffsetListOf<PosLookup> PosLookupList;
1524 ASSERT_SIZE (PosLookupList, 2);
1530 struct GPOS : GSUBGPOS
1532 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1534 inline const PosLookup& get_lookup (unsigned int i) const
1535 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1537 inline bool position_lookup (hb_ot_layout_context_t *layout_context,
1538 hb_buffer_t *buffer,
1539 unsigned int lookup_index,
1540 hb_mask_t mask) const
1541 { return get_lookup (lookup_index).apply_string (layout_context, buffer, mask); }
1543 inline bool sanitize (SANITIZE_ARG_DEF) {
1545 if (HB_UNLIKELY (!GSUBGPOS::sanitize (SANITIZE_ARG))) return false;
1546 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1547 return SANITIZE_THIS (list);
1550 ASSERT_SIZE (GPOS, 10);
1553 /* Out-of-class implementation for methods recursing */
1555 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1558 return get_subtable ().apply (APPLY_ARG, get_type ());
1561 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1564 if (HB_UNLIKELY (!Extension::sanitize (SANITIZE_ARG))) return false;
1565 unsigned int offset = get_offset ();
1566 if (HB_UNLIKELY (!offset)) return true;
1567 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1570 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1572 const GPOS &gpos = *(layout_context->face->ot_layout.gpos);
1573 const PosLookup &l = gpos.get_lookup (lookup_index);
1575 if (HB_UNLIKELY (context->nesting_level_left == 0))
1578 if (HB_UNLIKELY (context_length < 1))
1581 return l.apply_once (layout_context, buffer, context_length, context->nesting_level_left - 1, apply_depth + 1);
1585 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */