2 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
4 * This is part of HarfBuzz, an OpenType Layout engine 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[0];
39 ASSERT_SIZE (ValueRecord, 0);
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 */
57 inline unsigned int get_len () const
58 { return _hb_popcount32 ((unsigned int) *this); }
59 inline unsigned int get_size () const
60 { return get_len () * sizeof (Value); }
62 void apply_value (hb_ot_layout_context_t *context,
65 hb_internal_glyph_position_t *glyph_pos) const
67 unsigned int x_ppem, y_ppem;
68 hb_16dot16_t x_scale, y_scale;
69 unsigned int format = *this;
74 /* All fields are options. Only those available advance the value
78 SHORT xPlacement; /* Horizontal adjustment for
79 * placement--in design units */
80 SHORT yPlacement; /* Vertical adjustment for
81 * placement--in design units */
82 SHORT xAdvance; /* Horizontal adjustment for
83 * advance--in design units (only used
84 * for horizontal writing) */
85 SHORT yAdvance; /* Vertical adjustment for advance--in
86 * design units (only used for vertical
88 Offset xPlaDevice; /* Offset to Device table for
89 * horizontal placement--measured from
90 * beginning of PosTable (may be NULL) */
91 Offset yPlaDevice; /* Offset to Device table for vertical
92 * placement--measured from beginning
93 * of PosTable (may be NULL) */
94 Offset xAdvDevice; /* Offset to Device table for
95 * horizontal advance--measured from
96 * beginning of PosTable (may be NULL) */
97 Offset yAdvDevice; /* Offset to Device table for vertical
98 * advance--measured from beginning of
99 * PosTable (may be NULL) */
103 x_scale = context->font->x_scale;
104 y_scale = context->font->y_scale;
105 /* design units -> fractional pixel */
106 if (format & xPlacement)
107 glyph_pos->x_pos += _hb_16dot16_mul_trunc (x_scale, *(SHORT*)values++);
108 if (format & yPlacement)
109 glyph_pos->y_pos += _hb_16dot16_mul_trunc (y_scale, *(SHORT*)values++);
110 if (format & xAdvance)
111 glyph_pos->x_advance += _hb_16dot16_mul_trunc (x_scale, *(SHORT*)values++);
112 if (format & yAdvance)
113 glyph_pos->y_advance += _hb_16dot16_mul_trunc (y_scale, *(SHORT*)values++);
115 x_ppem = context->font->x_ppem;
116 y_ppem = context->font->y_ppem;
117 /* pixel -> fractional pixel */
118 if (format & xPlaDevice) {
120 glyph_pos->x_pos += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
124 if (format & yPlaDevice) {
126 glyph_pos->y_pos += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
130 if (format & xAdvDevice) {
132 glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
136 if (format & yAdvDevice) {
138 glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
144 ASSERT_SIZE (ValueFormat, 2);
149 friend struct Anchor;
152 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
153 hb_position_t *x, hb_position_t *y) const
155 *x = _hb_16dot16_mul_trunc (context->font->x_scale, xCoordinate);
156 *y = _hb_16dot16_mul_trunc (context->font->y_scale, yCoordinate);
159 inline bool sanitize (SANITIZE_ARG_DEF) {
161 return SANITIZE_SELF ();
165 USHORT format; /* Format identifier--format = 1 */
166 SHORT xCoordinate; /* Horizontal value--in design units */
167 SHORT yCoordinate; /* Vertical value--in design units */
169 ASSERT_SIZE (AnchorFormat1, 6);
173 friend struct Anchor;
176 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
177 hb_position_t *x, hb_position_t *y) const
180 *x = _hb_16dot16_mul_trunc (context->font->x_scale, xCoordinate);
181 *y = _hb_16dot16_mul_trunc (context->font->y_scale, yCoordinate);
184 inline bool sanitize (SANITIZE_ARG_DEF) {
186 return SANITIZE_SELF ();
190 USHORT format; /* Format identifier--format = 2 */
191 SHORT xCoordinate; /* Horizontal value--in design units */
192 SHORT yCoordinate; /* Vertical value--in design units */
193 USHORT anchorPoint; /* Index to glyph contour point */
195 ASSERT_SIZE (AnchorFormat2, 8);
199 friend struct Anchor;
202 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
203 hb_position_t *x, hb_position_t *y) const
205 *x = _hb_16dot16_mul_trunc (context->font->x_scale, xCoordinate);
206 *y = _hb_16dot16_mul_trunc (context->font->y_scale, yCoordinate);
208 if (context->font->x_ppem)
209 *x += (this+xDeviceTable).get_delta (context->font->x_ppem) << 6;
210 if (context->font->y_ppem)
211 *y += (this+yDeviceTable).get_delta (context->font->y_ppem) << 6;
214 inline bool sanitize (SANITIZE_ARG_DEF) {
216 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
220 USHORT format; /* Format identifier--format = 3 */
221 SHORT xCoordinate; /* Horizontal value--in design units */
222 SHORT yCoordinate; /* Vertical value--in design units */
224 xDeviceTable; /* Offset to Device table for X
225 * coordinate-- from beginning of
226 * Anchor table (may be NULL) */
228 yDeviceTable; /* Offset to Device table for Y
229 * coordinate-- from beginning of
230 * Anchor table (may be NULL) */
232 ASSERT_SIZE (AnchorFormat3, 10);
236 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
237 hb_position_t *x, hb_position_t *y) const
241 case 1: u.format1->get_anchor (context, glyph_id, x, y); return;
242 case 2: u.format2->get_anchor (context, glyph_id, x, y); return;
243 case 3: u.format3->get_anchor (context, glyph_id, x, y); return;
248 inline bool sanitize (SANITIZE_ARG_DEF) {
250 if (!SANITIZE (u.format)) return false;
252 case 1: return u.format1->sanitize (SANITIZE_ARG);
253 case 2: return u.format2->sanitize (SANITIZE_ARG);
254 case 3: return u.format3->sanitize (SANITIZE_ARG);
261 USHORT format; /* Format identifier */
262 AnchorFormat1 format1[];
263 AnchorFormat2 format2[];
264 AnchorFormat3 format3[];
267 ASSERT_SIZE (Anchor, 2);
272 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
273 if (HB_UNLIKELY (row >= rows || col >= cols)) return Null(Anchor);
274 return this+matrix[row * cols + col];
277 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
279 if (!SANITIZE_SELF ()) return false;
280 unsigned int count = rows * cols;
281 if (!SANITIZE_ARRAY (matrix, sizeof (matrix[0]), count)) return false;
282 for (unsigned int i = 0; i < count; i++)
283 if (!SANITIZE_THIS (matrix[i])) return false;
287 USHORT rows; /* Number of rows */
290 matrix[]; /* Matrix of offsets to Anchor tables--
291 * from beginning of AnchorMatrix table */
293 ASSERT_SIZE (AnchorMatrix, 2);
298 friend struct MarkArray;
300 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
302 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
306 USHORT klass; /* Class defined for this mark */
308 markAnchor; /* Offset to Anchor table--from
309 * beginning of MarkArray table */
311 ASSERT_SIZE (MarkRecord, 4);
315 inline bool apply (APPLY_ARG_DEF,
316 unsigned int mark_index, unsigned int glyph_index,
317 const AnchorMatrix &anchors, unsigned int class_count,
318 unsigned int glyph_pos) const
321 const MarkRecord &record = markRecord[mark_index];
322 unsigned int mark_class = record.klass;
324 const Anchor& mark_anchor = this + record.markAnchor;
325 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
327 hb_position_t mark_x, mark_y, base_x, base_y;
329 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
330 glyph_anchor.get_anchor (context, IN_GLYPH (glyph_pos), &base_x, &base_y);
332 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
333 o->x_pos = base_x - mark_x;
334 o->y_pos = base_y - mark_y;
337 o->back = buffer->in_pos - glyph_pos;
343 inline bool sanitize (SANITIZE_ARG_DEF) {
345 return SANITIZE_THIS (markRecord);
350 markRecord; /* Array of MarkRecords--in Coverage order */
352 ASSERT_SIZE (MarkArray, 2);
357 struct SinglePosFormat1
359 friend struct SinglePos;
362 inline bool apply (APPLY_ARG_DEF) const
365 unsigned int index = (this+coverage) (IN_CURGLYPH ());
366 if (HB_LIKELY (index == NOT_COVERED))
369 valueFormat.apply_value (context, CONST_CHARP(this), values, CURPOSITION ());
375 inline bool sanitize (SANITIZE_ARG_DEF) {
377 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
378 SANITIZE_MEM (values, valueFormat.get_size ());
382 USHORT format; /* Format identifier--format = 1 */
384 coverage; /* Offset to Coverage table--from
385 * beginning of subtable */
386 ValueFormat valueFormat; /* Defines the types of data in the
388 ValueRecord values; /* Defines positioning
389 * value(s)--applied to all glyphs in
390 * the Coverage table */
392 ASSERT_SIZE (SinglePosFormat1, 6);
394 struct SinglePosFormat2
396 friend struct SinglePos;
399 inline bool apply (APPLY_ARG_DEF) const
402 unsigned int index = (this+coverage) (IN_CURGLYPH ());
403 if (HB_LIKELY (index == NOT_COVERED))
406 if (HB_LIKELY (index >= valueCount))
409 valueFormat.apply_value (context, CONST_CHARP(this),
410 values + index * valueFormat.get_len (),
417 inline bool sanitize (SANITIZE_ARG_DEF) {
419 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
420 SANITIZE_MEM (values, valueFormat.get_size () * valueCount);
424 USHORT format; /* Format identifier--format = 2 */
426 coverage; /* Offset to Coverage table--from
427 * beginning of subtable */
428 ValueFormat valueFormat; /* Defines the types of data in the
430 USHORT valueCount; /* Number of ValueRecords */
431 ValueRecord values; /* Array of ValueRecords--positioning
432 * values applied to glyphs */
434 ASSERT_SIZE (SinglePosFormat2, 8);
438 friend struct PosLookupSubTable;
441 inline bool apply (APPLY_ARG_DEF) const
445 case 1: return u.format1->apply (APPLY_ARG);
446 case 2: return u.format2->apply (APPLY_ARG);
447 default:return false;
451 inline bool sanitize (SANITIZE_ARG_DEF) {
453 if (!SANITIZE (u.format)) return false;
455 case 1: return u.format1->sanitize (SANITIZE_ARG);
456 case 2: return u.format2->sanitize (SANITIZE_ARG);
463 USHORT format; /* Format identifier */
464 SinglePosFormat1 format1[];
465 SinglePosFormat2 format2[];
468 ASSERT_SIZE (SinglePos, 2);
471 struct PairValueRecord
473 friend struct PairPosFormat1;
476 GlyphID secondGlyph; /* GlyphID of second glyph in the
477 * pair--first glyph is listed in the
479 ValueRecord values; /* Positioning data for the first glyph
480 * followed by for second glyph */
482 ASSERT_SIZE (PairValueRecord, 2);
486 friend struct PairPosFormat1;
488 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
490 if (!SANITIZE_SELF ()) return false;
491 unsigned int count = (1 + format_len) * len;
492 return SANITIZE_MEM (array, sizeof (array[0]) * count);
496 USHORT len; /* Number of PairValueRecords */
498 array[]; /* Array of PairValueRecords--ordered
499 * by GlyphID of the second glyph */
501 ASSERT_SIZE (PairSet, 2);
503 struct PairPosFormat1
505 friend struct PairPos;
508 inline bool apply (APPLY_ARG_DEF) const
511 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
512 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
515 unsigned int index = (this+coverage) (IN_CURGLYPH ());
516 if (HB_LIKELY (index == NOT_COVERED))
519 unsigned int j = buffer->in_pos + 1;
520 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
522 if (HB_UNLIKELY (j == end))
527 const PairSet &pair_set = this+pairSet[index];
529 unsigned int len1 = valueFormat1.get_len ();
530 unsigned int len2 = valueFormat2.get_len ();
531 unsigned int record_len = 1 + len1 + len2;
533 unsigned int count = pair_set.len;
534 const PairValueRecord *record = pair_set.array;
535 for (unsigned int i = 0; i < count; i++)
537 if (IN_GLYPH (j) == record->secondGlyph)
539 valueFormat1.apply_value (context, CONST_CHARP(this), record->values, CURPOSITION ());
540 valueFormat2.apply_value (context, CONST_CHARP(this), record->values + len1, POSITION (j));
546 record += record_len;
552 inline bool sanitize (SANITIZE_ARG_DEF) {
554 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
555 pairSet.sanitize (SANITIZE_ARG, CONST_CHARP(this),
556 valueFormat1.get_len () + valueFormat2.get_len ());
560 USHORT format; /* Format identifier--format = 1 */
562 coverage; /* Offset to Coverage table--from
563 * beginning of subtable */
564 ValueFormat valueFormat1; /* Defines the types of data in
565 * ValueRecord1--for the first glyph
566 * in the pair--may be zero (0) */
567 ValueFormat valueFormat2; /* Defines the types of data in
568 * ValueRecord2--for the second glyph
569 * in the pair--may be zero (0) */
570 OffsetArrayOf<PairSet>
571 pairSet; /* Array of PairSet tables
572 * ordered by Coverage Index */
574 ASSERT_SIZE (PairPosFormat1, 10);
576 struct PairPosFormat2
578 friend struct PairPos;
581 inline bool apply (APPLY_ARG_DEF) const
584 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
585 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
588 unsigned int index = (this+coverage) (IN_CURGLYPH ());
589 if (HB_LIKELY (index == NOT_COVERED))
592 unsigned int j = buffer->in_pos + 1;
593 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
595 if (HB_UNLIKELY (j == end))
600 unsigned int len1 = valueFormat1.get_len ();
601 unsigned int len2 = valueFormat2.get_len ();
602 unsigned int record_len = len1 + len2;
604 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
605 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
606 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
609 const Value *v = values + record_len * (klass1 * class2Count + klass2);
610 valueFormat1.apply_value (context, CONST_CHARP(this), v, CURPOSITION ());
611 valueFormat2.apply_value (context, CONST_CHARP(this), v + len1, POSITION (j));
620 inline bool sanitize (SANITIZE_ARG_DEF) {
622 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
623 SANITIZE_THIS2 (classDef1, classDef2))) return false;
625 unsigned int record_size =valueFormat1.get_size () + valueFormat2.get_size ();
626 unsigned int len = class1Count * class2Count;
627 return SANITIZE_ARRAY (values, record_size, len);
631 USHORT format; /* Format identifier--format = 2 */
633 coverage; /* Offset to Coverage table--from
634 * beginning of subtable */
635 ValueFormat valueFormat1; /* ValueRecord definition--for the
636 * first glyph of the pair--may be zero
638 ValueFormat valueFormat2; /* ValueRecord definition--for the
639 * second glyph of the pair--may be
642 classDef1; /* Offset to ClassDef table--from
643 * beginning of PairPos subtable--for
644 * the first glyph of the pair */
646 classDef2; /* Offset to ClassDef table--from
647 * beginning of PairPos subtable--for
648 * the second glyph of the pair */
649 USHORT class1Count; /* Number of classes in ClassDef1
650 * table--includes Class0 */
651 USHORT class2Count; /* Number of classes in ClassDef2
652 * table--includes Class0 */
653 ValueRecord values; /* Matrix of value pairs:
654 * class1-major, class2-minor,
655 * Each entry has value1 and value2 */
657 ASSERT_SIZE (PairPosFormat2, 16);
661 friend struct PosLookupSubTable;
664 inline bool apply (APPLY_ARG_DEF) const
668 case 1: return u.format1->apply (APPLY_ARG);
669 case 2: return u.format2->apply (APPLY_ARG);
670 default:return false;
674 inline bool sanitize (SANITIZE_ARG_DEF) {
676 if (!SANITIZE (u.format)) return false;
678 case 1: return u.format1->sanitize (SANITIZE_ARG);
679 case 2: return u.format2->sanitize (SANITIZE_ARG);
686 USHORT format; /* Format identifier */
687 PairPosFormat1 format1[];
688 PairPosFormat2 format2[];
691 ASSERT_SIZE (PairPos, 2);
694 struct EntryExitRecord
696 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
698 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
702 entryAnchor; /* Offset to EntryAnchor table--from
703 * beginning of CursivePos
704 * subtable--may be NULL */
706 exitAnchor; /* Offset to ExitAnchor table--from
707 * beginning of CursivePos
708 * subtable--may be NULL */
710 ASSERT_SIZE (EntryExitRecord, 4);
712 struct CursivePosFormat1
714 friend struct CursivePos;
717 inline bool apply (APPLY_ARG_DEF) const
720 /* Now comes the messiest part of the whole OpenType
721 specification. At first glance, cursive connections seem easy
722 to understand, but there are pitfalls! The reason is that
723 the specs don't mention how to compute the advance values
724 resp. glyph offsets. I was told it would be an omission, to
725 be fixed in the next OpenType version... Again many thanks to
726 Andrei Burago <andreib@microsoft.com> for clarifications.
728 Consider the following example:
741 glyph1: advance width = 12
744 glyph2: advance width = 11
747 LSB is 1 for both glyphs (so the boxes drawn above are glyph
748 bboxes). Writing direction is R2L; `0' denotes the glyph's
751 Now the surprising part: The advance width of the *left* glyph
752 (resp. of the *bottom* glyph) will be modified, no matter
753 whether the writing direction is L2R or R2L (resp. T2B or
754 B2T)! This assymetry is caused by the fact that the glyph's
755 coordinate origin is always the lower left corner for all
758 Continuing the above example, we can compute the new
759 (horizontal) advance width of glyph2 as
763 and the new vertical offset of glyph2 as
768 Vertical writing direction is far more complicated:
770 a) Assuming that we recompute the advance height of the lower glyph:
777 yadv2 | 0+--+------+ -- BSB1 --
780 BSB2 -- 0+--------+ --
783 glyph1: advance height = 6
786 glyph2: advance height = 7
789 TSB is 1 for both glyphs; writing direction is T2B.
792 BSB1 = yadv1 - (TSB1 + ymax1)
793 BSB2 = yadv2 - (TSB2 + ymax2)
796 vertical advance width of glyph2
797 = y_offset + BSB2 - BSB1
798 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
799 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
800 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
803 b) Assuming that we recompute the advance height of the upper glyph:
808 TSB2 -- +-----+--+ 1 | yadv1 ymax1
810 yadv2 | 0+--+------+ -- --
811 ymax2 | 2 | -- y_offset
816 glyph1: advance height = 6
819 glyph2: advance height = 7
822 TSB is 1 for both glyphs; writing direction is T2B.
826 vertical advance width of glyph2
827 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
828 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
831 Comparing a) with b) shows that b) is easier to compute. I'll wait
832 for a reply from Andrei to see what should really be implemented...
834 Since horizontal advance widths or vertical advance heights
835 can be used alone but not together, no ambiguity occurs. */
837 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->info.gpos;
838 hb_codepoint_t last_pos = gpi->last;
839 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
841 /* We don't handle mark glyphs here. */
842 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
845 unsigned int index = (this+coverage) (IN_CURGLYPH ());
846 if (HB_LIKELY (index == NOT_COVERED))
849 const EntryExitRecord &record = entryExitRecord[index];
851 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
854 hb_position_t entry_x, entry_y;
855 (this+record.entryAnchor).get_anchor (context, IN_CURGLYPH (), &entry_x, &entry_y);
859 if (buffer->direction == HB_DIRECTION_RTL)
861 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
862 POSITION (buffer->in_pos)->new_advance = TRUE;
866 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
867 POSITION (last_pos)->new_advance = TRUE;
870 if (lookup_flag & LookupFlag::RightToLeft)
872 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
873 POSITION (last_pos)->y_pos = entry_y - gpi->anchor_y;
877 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
878 POSITION (buffer->in_pos)->y_pos = gpi->anchor_y - entry_y;
882 if (record.exitAnchor)
884 gpi->last = buffer->in_pos;
885 (this+record.exitAnchor).get_anchor (context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
892 inline bool sanitize (SANITIZE_ARG_DEF) {
894 return SANITIZE_THIS2 (coverage, entryExitRecord);
898 USHORT format; /* Format identifier--format = 1 */
900 coverage; /* Offset to Coverage table--from
901 * beginning of subtable */
902 ArrayOf<EntryExitRecord>
903 entryExitRecord; /* Array of EntryExit records--in
904 * Coverage Index order */
906 ASSERT_SIZE (CursivePosFormat1, 6);
910 friend struct PosLookupSubTable;
913 inline bool apply (APPLY_ARG_DEF) const
917 case 1: return u.format1->apply (APPLY_ARG);
918 default:return false;
922 inline bool sanitize (SANITIZE_ARG_DEF) {
924 if (!SANITIZE (u.format)) return false;
926 case 1: return u.format1->sanitize (SANITIZE_ARG);
933 USHORT format; /* Format identifier */
934 CursivePosFormat1 format1[];
937 ASSERT_SIZE (CursivePos, 2);
940 typedef AnchorMatrix BaseArray; /* base-major--
941 * in order of BaseCoverage Index--,
943 * ordered by class--zero-based. */
944 ASSERT_SIZE (BaseArray, 2);
946 struct MarkBasePosFormat1
948 friend struct MarkBasePos;
951 inline bool apply (APPLY_ARG_DEF) const
954 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
955 if (HB_LIKELY (mark_index == NOT_COVERED))
958 /* now we search backwards for a non-mark glyph */
959 unsigned int j = buffer->in_pos;
962 if (HB_UNLIKELY (!j))
965 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
968 /* The following assertion is too strong. */
969 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
973 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
974 if (base_index == NOT_COVERED)
977 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
980 inline bool sanitize (SANITIZE_ARG_DEF) {
982 return SANITIZE_SELF () && SANITIZE_THIS2 (markCoverage, baseCoverage) &&
983 SANITIZE_THIS (markArray) && baseArray.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
987 USHORT format; /* Format identifier--format = 1 */
989 markCoverage; /* Offset to MarkCoverage table--from
990 * beginning of MarkBasePos subtable */
992 baseCoverage; /* Offset to BaseCoverage table--from
993 * beginning of MarkBasePos subtable */
994 USHORT classCount; /* Number of classes defined for marks */
996 markArray; /* Offset to MarkArray table--from
997 * beginning of MarkBasePos subtable */
999 baseArray; /* Offset to BaseArray table--from
1000 * beginning of MarkBasePos subtable */
1002 ASSERT_SIZE (MarkBasePosFormat1, 12);
1006 friend struct PosLookupSubTable;
1009 inline bool apply (APPLY_ARG_DEF) const
1013 case 1: return u.format1->apply (APPLY_ARG);
1014 default:return false;
1018 inline bool sanitize (SANITIZE_ARG_DEF) {
1020 if (!SANITIZE (u.format)) return false;
1022 case 1: return u.format1->sanitize (SANITIZE_ARG);
1023 default:return true;
1029 USHORT format; /* Format identifier */
1030 MarkBasePosFormat1 format1[];
1033 ASSERT_SIZE (MarkBasePos, 2);
1036 typedef AnchorMatrix LigatureAttach; /* component-major--
1037 * in order of writing direction--,
1039 * ordered by class--zero-based. */
1040 ASSERT_SIZE (LigatureAttach, 2);
1042 typedef OffsetListOf<LigatureAttach> LigatureArray;
1043 /* Array of LigatureAttach
1045 * LigatureCoverage Index */
1046 ASSERT_SIZE (LigatureArray, 2);
1048 struct MarkLigPosFormat1
1050 friend struct MarkLigPos;
1053 inline bool apply (APPLY_ARG_DEF) const
1056 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1057 if (HB_LIKELY (mark_index == NOT_COVERED))
1060 /* now we search backwards for a non-mark glyph */
1061 unsigned int j = buffer->in_pos;
1064 if (HB_UNLIKELY (!j))
1067 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1070 /* The following assertion is too strong. */
1071 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1075 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1076 if (lig_index == NOT_COVERED)
1079 const LigatureArray& lig_array = this+ligatureArray;
1080 const LigatureAttach& lig_attach = lig_array[lig_index];
1082 /* Find component to attach to */
1083 unsigned int comp_count = lig_attach.rows;
1084 if (HB_UNLIKELY (!comp_count))
1086 unsigned int comp_index;
1087 /* We must now check whether the ligature ID of the current mark glyph
1088 * is identical to the ligature ID of the found ligature. If yes, we
1089 * can directly use the component index. If not, we attach the mark
1090 * glyph to the last component of the ligature. */
1091 if (IN_LIGID (j) == IN_LIGID (buffer->in_pos))
1093 comp_index = IN_COMPONENT (buffer->in_pos);
1094 if (comp_index >= comp_count)
1095 comp_index = comp_count - 1;
1098 comp_index = comp_count - 1;
1100 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1103 inline bool sanitize (SANITIZE_ARG_DEF) {
1105 return SANITIZE_SELF () &&
1106 SANITIZE_THIS2 (markCoverage, ligatureCoverage) &&
1107 SANITIZE_THIS (markArray) && ligatureArray.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
1111 USHORT format; /* Format identifier--format = 1 */
1113 markCoverage; /* Offset to Mark Coverage table--from
1114 * beginning of MarkLigPos subtable */
1116 ligatureCoverage; /* Offset to Ligature Coverage
1117 * table--from beginning of MarkLigPos
1119 USHORT classCount; /* Number of defined mark classes */
1121 markArray; /* Offset to MarkArray table--from
1122 * beginning of MarkLigPos subtable */
1123 OffsetTo<LigatureArray>
1124 ligatureArray; /* Offset to LigatureArray table--from
1125 * beginning of MarkLigPos subtable */
1127 ASSERT_SIZE (MarkLigPosFormat1, 12);
1131 friend struct PosLookupSubTable;
1134 inline bool apply (APPLY_ARG_DEF) const
1138 case 1: return u.format1->apply (APPLY_ARG);
1139 default:return false;
1143 inline bool sanitize (SANITIZE_ARG_DEF) {
1145 if (!SANITIZE (u.format)) return false;
1147 case 1: return u.format1->sanitize (SANITIZE_ARG);
1148 default:return true;
1154 USHORT format; /* Format identifier */
1155 MarkLigPosFormat1 format1[];
1158 ASSERT_SIZE (MarkLigPos, 2);
1161 typedef AnchorMatrix Mark2Array; /* mark2-major--
1162 * in order of Mark2Coverage Index--,
1164 * ordered by class--zero-based. */
1165 ASSERT_SIZE (Mark2Array, 2);
1167 struct MarkMarkPosFormat1
1169 friend struct MarkMarkPos;
1172 inline bool apply (APPLY_ARG_DEF) const
1175 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1176 if (HB_LIKELY (mark1_index == NOT_COVERED))
1179 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1180 unsigned int j = buffer->in_pos;
1183 if (HB_UNLIKELY (!j))
1186 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property));
1188 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1191 /* Two marks match only if they belong to the same base, or same component
1192 * of the same ligature. */
1193 if (IN_LIGID (j) != IN_LIGID (buffer->in_pos) ||
1194 IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos))
1197 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1198 if (mark2_index == NOT_COVERED)
1201 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1204 inline bool sanitize (SANITIZE_ARG_DEF) {
1206 return SANITIZE_SELF () && SANITIZE_THIS2 (mark1Coverage, mark2Coverage) &&
1207 SANITIZE_THIS (mark1Array) && mark2Array.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
1211 USHORT format; /* Format identifier--format = 1 */
1213 mark1Coverage; /* Offset to Combining Mark1 Coverage
1214 * table--from beginning of MarkMarkPos
1217 mark2Coverage; /* Offset to Combining Mark2 Coverage
1218 * table--from beginning of MarkMarkPos
1220 USHORT classCount; /* Number of defined mark classes */
1222 mark1Array; /* Offset to Mark1Array table--from
1223 * beginning of MarkMarkPos subtable */
1224 OffsetTo<Mark2Array>
1225 mark2Array; /* Offset to Mark2Array table--from
1226 * beginning of MarkMarkPos subtable */
1228 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1232 friend struct PosLookupSubTable;
1235 inline bool apply (APPLY_ARG_DEF) const
1239 case 1: return u.format1->apply (APPLY_ARG);
1240 default:return false;
1244 inline bool sanitize (SANITIZE_ARG_DEF) {
1246 if (!SANITIZE (u.format)) return false;
1248 case 1: return u.format1->sanitize (SANITIZE_ARG);
1249 default:return true;
1255 USHORT format; /* Format identifier */
1256 MarkMarkPosFormat1 format1[];
1259 ASSERT_SIZE (MarkMarkPos, 2);
1262 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1264 struct ContextPos : Context
1266 friend struct PosLookupSubTable;
1269 inline bool apply (APPLY_ARG_DEF) const
1272 return Context::apply (APPLY_ARG, position_lookup);
1275 ASSERT_SIZE (ContextPos, 2);
1277 struct ChainContextPos : ChainContext
1279 friend struct PosLookupSubTable;
1282 inline bool apply (APPLY_ARG_DEF) const
1285 return ChainContext::apply (APPLY_ARG, position_lookup);
1288 ASSERT_SIZE (ChainContextPos, 2);
1291 struct ExtensionPos : Extension
1293 friend struct PosLookupSubTable;
1296 inline const struct PosLookupSubTable& get_subtable (void) const
1297 { return CONST_CAST (PosLookupSubTable, Extension::get_subtable (), 0); }
1299 inline bool apply (APPLY_ARG_DEF) const;
1301 inline bool sanitize (SANITIZE_ARG_DEF);
1303 ASSERT_SIZE (ExtensionPos, 2);
1312 struct PosLookupSubTable
1314 friend struct PosLookup;
1328 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1331 switch (lookup_type) {
1332 case Single: return u.single->apply (APPLY_ARG);
1333 case Pair: return u.pair->apply (APPLY_ARG);
1334 case Cursive: return u.cursive->apply (APPLY_ARG);
1335 case MarkBase: return u.markBase->apply (APPLY_ARG);
1336 case MarkLig: return u.markLig->apply (APPLY_ARG);
1337 case MarkMark: return u.markMark->apply (APPLY_ARG);
1338 case Context: return u.context->apply (APPLY_ARG);
1339 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1340 case Extension: return u.extension->apply (APPLY_ARG);
1341 default:return false;
1345 inline bool sanitize (SANITIZE_ARG_DEF) {
1347 if (!SANITIZE (u.format)) return false;
1349 case Single: return u.single->sanitize (SANITIZE_ARG);
1350 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1351 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1352 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1353 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1354 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1355 case Context: return u.context->sanitize (SANITIZE_ARG);
1356 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1357 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1358 default:return true;
1367 CursivePos cursive[];
1368 MarkBasePos markBase[];
1369 MarkLigPos markLig[];
1370 MarkMarkPos markMark[];
1371 ContextPos context[];
1372 ChainContextPos chainContext[];
1373 ExtensionPos extension[];
1376 ASSERT_SIZE (PosLookupSubTable, 2);
1379 struct PosLookup : Lookup
1381 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1382 { return (const PosLookupSubTable&) Lookup::get_subtable (i); }
1384 /* Like get_type(), but looks through extension lookups.
1385 * Never returns Extension */
1386 inline unsigned int get_effective_type (void) const
1388 unsigned int type = get_type ();
1390 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
1392 unsigned int count = get_subtable_count ();
1393 type = get_subtable(0).u.extension->get_type ();
1394 /* The spec says all subtables should have the same type.
1395 * This is specially important if one has a reverse type! */
1396 for (unsigned int i = 1; i < count; i++)
1397 if (get_subtable(i).u.extension->get_type () != type)
1404 inline bool apply_once (hb_ot_layout_context_t *context,
1405 hb_buffer_t *buffer,
1406 unsigned int context_length,
1407 unsigned int nesting_level_left) const
1409 unsigned int lookup_type = get_type ();
1410 unsigned int lookup_flag = get_flag ();
1411 unsigned int property;
1413 if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
1416 for (unsigned int i = 0; i < get_subtable_count (); i++)
1417 if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
1423 inline bool apply_string (hb_ot_layout_context_t *context,
1424 hb_buffer_t *buffer,
1425 hb_mask_t mask) const
1429 if (HB_UNLIKELY (!buffer->in_length))
1432 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1435 while (buffer->in_pos < buffer->in_length)
1438 if (~IN_MASK (buffer->in_pos) & mask)
1440 done = apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1446 /* Contrary to properties defined in GDEF, user-defined properties
1447 will always stop a possible cursive positioning. */
1448 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1458 inline bool sanitize (SANITIZE_ARG_DEF) {
1460 if (!Lookup::sanitize (SANITIZE_ARG)) return false;
1461 OffsetArrayOf<PosLookupSubTable> &list = (OffsetArrayOf<PosLookupSubTable> &) subTable;
1462 return SANITIZE_THIS (list);
1465 ASSERT_SIZE (PosLookup, 6);
1467 typedef OffsetListOf<PosLookup> PosLookupList;
1468 ASSERT_SIZE (PosLookupList, 2);
1474 struct GPOS : GSUBGPOS
1476 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1478 static inline const GPOS& get_for_data (const char *data)
1479 { return (const GPOS&) GSUBGPOS::get_for_data (data); }
1481 inline const PosLookup& get_lookup (unsigned int i) const
1482 { return (const PosLookup&) GSUBGPOS::get_lookup (i); }
1484 inline bool position_lookup (hb_ot_layout_context_t *context,
1485 hb_buffer_t *buffer,
1486 unsigned int lookup_index,
1487 hb_mask_t mask) const
1488 { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
1490 inline bool sanitize (SANITIZE_ARG_DEF) {
1492 if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
1493 OffsetTo<PosLookupList> &list = CAST(OffsetTo<PosLookupList>, lookupList, 0);
1494 return SANITIZE_THIS (list);
1497 ASSERT_SIZE (GPOS, 10);
1500 /* Out-of-class implementation for methods recursing */
1502 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1505 unsigned int lookup_type = get_type ();
1507 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
1510 return get_subtable ().apply (APPLY_ARG, lookup_type);
1513 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1516 return Extension::sanitize (SANITIZE_ARG) &&
1517 (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
1518 get_type () == PosLookupSubTable::Extension ||
1519 DECONST_CAST (PosLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
1522 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1524 const GPOS &gpos = *(context->face->ot_layout.gpos);
1525 const PosLookup &l = gpos.get_lookup (lookup_index);
1527 if (HB_UNLIKELY (nesting_level_left == 0))
1529 nesting_level_left--;
1531 if (HB_UNLIKELY (context_length < 1))
1534 return l.apply_once (context, buffer, context_length, nesting_level_left);
1538 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */