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[VAR];
39 ASSERT_SIZE_VAR (Value, 0, ValueRecord);
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[VAR];
263 AnchorFormat2 format2[VAR];
264 AnchorFormat3 format3[VAR];
271 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
272 if (HB_UNLIKELY (row >= rows || col >= cols)) return Null(Anchor);
273 return this+matrix[row * cols + col];
276 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
278 if (!SANITIZE_SELF ()) return false;
279 unsigned int count = rows * cols;
280 if (!SANITIZE_ARRAY (matrix, sizeof (matrix[0]), count)) return false;
281 for (unsigned int i = 0; i < count; i++)
282 if (!SANITIZE_THIS (matrix[i])) return false;
286 USHORT rows; /* Number of rows */
289 matrix[VAR]; /* Matrix of offsets to Anchor tables--
290 * from beginning of AnchorMatrix table */
292 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
297 friend struct MarkArray;
299 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
301 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
305 USHORT klass; /* Class defined for this mark */
307 markAnchor; /* Offset to Anchor table--from
308 * beginning of MarkArray table */
310 ASSERT_SIZE (MarkRecord, 4);
314 inline bool apply (APPLY_ARG_DEF,
315 unsigned int mark_index, unsigned int glyph_index,
316 const AnchorMatrix &anchors, unsigned int class_count,
317 unsigned int glyph_pos) const
320 const MarkRecord &record = markRecord[mark_index];
321 unsigned int mark_class = record.klass;
323 const Anchor& mark_anchor = this + record.markAnchor;
324 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
326 hb_position_t mark_x, mark_y, base_x, base_y;
328 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
329 glyph_anchor.get_anchor (context, IN_GLYPH (glyph_pos), &base_x, &base_y);
331 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
332 o->x_pos = base_x - mark_x;
333 o->y_pos = base_y - mark_y;
336 o->back = buffer->in_pos - glyph_pos;
342 inline bool sanitize (SANITIZE_ARG_DEF) {
344 return SANITIZE_THIS (markRecord);
349 markRecord; /* Array of MarkRecords--in Coverage order */
351 ASSERT_SIZE (MarkArray, 2);
356 struct SinglePosFormat1
358 friend struct SinglePos;
361 inline bool apply (APPLY_ARG_DEF) const
364 unsigned int index = (this+coverage) (IN_CURGLYPH ());
365 if (HB_LIKELY (index == NOT_COVERED))
368 valueFormat.apply_value (context, CONST_CHARP(this), values, CURPOSITION ());
374 inline bool sanitize (SANITIZE_ARG_DEF) {
376 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
377 SANITIZE_MEM (values, valueFormat.get_size ());
381 USHORT format; /* Format identifier--format = 1 */
383 coverage; /* Offset to Coverage table--from
384 * beginning of subtable */
385 ValueFormat valueFormat; /* Defines the types of data in the
387 ValueRecord values; /* Defines positioning
388 * value(s)--applied to all glyphs in
389 * the Coverage table */
391 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
393 struct SinglePosFormat2
395 friend struct SinglePos;
398 inline bool apply (APPLY_ARG_DEF) const
401 unsigned int index = (this+coverage) (IN_CURGLYPH ());
402 if (HB_LIKELY (index == NOT_COVERED))
405 if (HB_LIKELY (index >= valueCount))
408 valueFormat.apply_value (context, CONST_CHARP(this),
409 values + index * valueFormat.get_len (),
416 inline bool sanitize (SANITIZE_ARG_DEF) {
418 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
419 SANITIZE_MEM (values, valueFormat.get_size () * valueCount);
423 USHORT format; /* Format identifier--format = 2 */
425 coverage; /* Offset to Coverage table--from
426 * beginning of subtable */
427 ValueFormat valueFormat; /* Defines the types of data in the
429 USHORT valueCount; /* Number of ValueRecords */
430 ValueRecord values; /* Array of ValueRecords--positioning
431 * values applied to glyphs */
433 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
437 friend struct PosLookupSubTable;
440 inline bool apply (APPLY_ARG_DEF) const
444 case 1: return u.format1->apply (APPLY_ARG);
445 case 2: return u.format2->apply (APPLY_ARG);
446 default:return false;
450 inline bool sanitize (SANITIZE_ARG_DEF) {
452 if (!SANITIZE (u.format)) return false;
454 case 1: return u.format1->sanitize (SANITIZE_ARG);
455 case 2: return u.format2->sanitize (SANITIZE_ARG);
462 USHORT format; /* Format identifier */
463 SinglePosFormat1 format1[VAR];
464 SinglePosFormat2 format2[VAR];
469 struct PairValueRecord
471 friend struct PairPosFormat1;
474 GlyphID secondGlyph; /* GlyphID of second glyph in the
475 * pair--first glyph is listed in the
477 ValueRecord values; /* Positioning data for the first glyph
478 * followed by for second glyph */
480 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
484 friend struct PairPosFormat1;
486 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
488 if (!SANITIZE_SELF ()) return false;
489 unsigned int count = (1 + format_len) * len;
490 return SANITIZE_MEM (array, sizeof (array[0]) * count);
494 USHORT len; /* Number of PairValueRecords */
496 array[VAR]; /* Array of PairValueRecords--ordered
497 * by GlyphID of the second glyph */
499 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
501 struct PairPosFormat1
503 friend struct PairPos;
506 inline bool apply (APPLY_ARG_DEF) const
509 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
510 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
513 unsigned int index = (this+coverage) (IN_CURGLYPH ());
514 if (HB_LIKELY (index == NOT_COVERED))
517 unsigned int j = buffer->in_pos + 1;
518 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
520 if (HB_UNLIKELY (j == end))
525 const PairSet &pair_set = this+pairSet[index];
527 unsigned int len1 = valueFormat1.get_len ();
528 unsigned int len2 = valueFormat2.get_len ();
529 unsigned int record_len = 1 + len1 + len2;
531 unsigned int count = pair_set.len;
532 const PairValueRecord *record = pair_set.array;
533 for (unsigned int i = 0; i < count; i++)
535 if (IN_GLYPH (j) == record->secondGlyph)
537 valueFormat1.apply_value (context, CONST_CHARP(this), record->values, CURPOSITION ());
538 valueFormat2.apply_value (context, CONST_CHARP(this), record->values + len1, POSITION (j));
544 record += record_len;
550 inline bool sanitize (SANITIZE_ARG_DEF) {
552 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
553 pairSet.sanitize (SANITIZE_ARG, CONST_CHARP(this),
554 valueFormat1.get_len () + valueFormat2.get_len ());
558 USHORT format; /* Format identifier--format = 1 */
560 coverage; /* Offset to Coverage table--from
561 * beginning of subtable */
562 ValueFormat valueFormat1; /* Defines the types of data in
563 * ValueRecord1--for the first glyph
564 * in the pair--may be zero (0) */
565 ValueFormat valueFormat2; /* Defines the types of data in
566 * ValueRecord2--for the second glyph
567 * in the pair--may be zero (0) */
568 OffsetArrayOf<PairSet>
569 pairSet; /* Array of PairSet tables
570 * ordered by Coverage Index */
572 ASSERT_SIZE (PairPosFormat1, 10);
574 struct PairPosFormat2
576 friend struct PairPos;
579 inline bool apply (APPLY_ARG_DEF) const
582 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
583 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
586 unsigned int index = (this+coverage) (IN_CURGLYPH ());
587 if (HB_LIKELY (index == NOT_COVERED))
590 unsigned int j = buffer->in_pos + 1;
591 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
593 if (HB_UNLIKELY (j == end))
598 unsigned int len1 = valueFormat1.get_len ();
599 unsigned int len2 = valueFormat2.get_len ();
600 unsigned int record_len = len1 + len2;
602 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
603 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
604 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
607 const Value *v = values + record_len * (klass1 * class2Count + klass2);
608 valueFormat1.apply_value (context, CONST_CHARP(this), v, CURPOSITION ());
609 valueFormat2.apply_value (context, CONST_CHARP(this), v + len1, POSITION (j));
618 inline bool sanitize (SANITIZE_ARG_DEF) {
620 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
621 SANITIZE_THIS2 (classDef1, classDef2))) return false;
623 unsigned int record_size =valueFormat1.get_size () + valueFormat2.get_size ();
624 unsigned int len = class1Count * class2Count;
625 return SANITIZE_ARRAY (values, record_size, len);
629 USHORT format; /* Format identifier--format = 2 */
631 coverage; /* Offset to Coverage table--from
632 * beginning of subtable */
633 ValueFormat valueFormat1; /* ValueRecord definition--for the
634 * first glyph of the pair--may be zero
636 ValueFormat valueFormat2; /* ValueRecord definition--for the
637 * second glyph of the pair--may be
640 classDef1; /* Offset to ClassDef table--from
641 * beginning of PairPos subtable--for
642 * the first glyph of the pair */
644 classDef2; /* Offset to ClassDef table--from
645 * beginning of PairPos subtable--for
646 * the second glyph of the pair */
647 USHORT class1Count; /* Number of classes in ClassDef1
648 * table--includes Class0 */
649 USHORT class2Count; /* Number of classes in ClassDef2
650 * table--includes Class0 */
651 ValueRecord values; /* Matrix of value pairs:
652 * class1-major, class2-minor,
653 * Each entry has value1 and value2 */
655 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
659 friend struct PosLookupSubTable;
662 inline bool apply (APPLY_ARG_DEF) const
666 case 1: return u.format1->apply (APPLY_ARG);
667 case 2: return u.format2->apply (APPLY_ARG);
668 default:return false;
672 inline bool sanitize (SANITIZE_ARG_DEF) {
674 if (!SANITIZE (u.format)) return false;
676 case 1: return u.format1->sanitize (SANITIZE_ARG);
677 case 2: return u.format2->sanitize (SANITIZE_ARG);
684 USHORT format; /* Format identifier */
685 PairPosFormat1 format1[VAR];
686 PairPosFormat2 format2[VAR];
691 struct EntryExitRecord
693 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
695 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
699 entryAnchor; /* Offset to EntryAnchor table--from
700 * beginning of CursivePos
701 * subtable--may be NULL */
703 exitAnchor; /* Offset to ExitAnchor table--from
704 * beginning of CursivePos
705 * subtable--may be NULL */
707 ASSERT_SIZE (EntryExitRecord, 4);
709 struct CursivePosFormat1
711 friend struct CursivePos;
714 inline bool apply (APPLY_ARG_DEF) const
717 /* Now comes the messiest part of the whole OpenType
718 specification. At first glance, cursive connections seem easy
719 to understand, but there are pitfalls! The reason is that
720 the specs don't mention how to compute the advance values
721 resp. glyph offsets. I was told it would be an omission, to
722 be fixed in the next OpenType version... Again many thanks to
723 Andrei Burago <andreib@microsoft.com> for clarifications.
725 Consider the following example:
738 glyph1: advance width = 12
741 glyph2: advance width = 11
744 LSB is 1 for both glyphs (so the boxes drawn above are glyph
745 bboxes). Writing direction is R2L; `0' denotes the glyph's
748 Now the surprising part: The advance width of the *left* glyph
749 (resp. of the *bottom* glyph) will be modified, no matter
750 whether the writing direction is L2R or R2L (resp. T2B or
751 B2T)! This assymetry is caused by the fact that the glyph's
752 coordinate origin is always the lower left corner for all
755 Continuing the above example, we can compute the new
756 (horizontal) advance width of glyph2 as
760 and the new vertical offset of glyph2 as
765 Vertical writing direction is far more complicated:
767 a) Assuming that we recompute the advance height of the lower glyph:
774 yadv2 | 0+--+------+ -- BSB1 --
777 BSB2 -- 0+--------+ --
780 glyph1: advance height = 6
783 glyph2: advance height = 7
786 TSB is 1 for both glyphs; writing direction is T2B.
789 BSB1 = yadv1 - (TSB1 + ymax1)
790 BSB2 = yadv2 - (TSB2 + ymax2)
793 vertical advance width of glyph2
794 = y_offset + BSB2 - BSB1
795 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
796 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
797 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
800 b) Assuming that we recompute the advance height of the upper glyph:
805 TSB2 -- +-----+--+ 1 | yadv1 ymax1
807 yadv2 | 0+--+------+ -- --
808 ymax2 | 2 | -- y_offset
813 glyph1: advance height = 6
816 glyph2: advance height = 7
819 TSB is 1 for both glyphs; writing direction is T2B.
823 vertical advance width of glyph2
824 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
825 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
828 Comparing a) with b) shows that b) is easier to compute. I'll wait
829 for a reply from Andrei to see what should really be implemented...
831 Since horizontal advance widths or vertical advance heights
832 can be used alone but not together, no ambiguity occurs. */
834 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->info.gpos;
835 hb_codepoint_t last_pos = gpi->last;
836 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
838 /* We don't handle mark glyphs here. */
839 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
842 unsigned int index = (this+coverage) (IN_CURGLYPH ());
843 if (HB_LIKELY (index == NOT_COVERED))
846 const EntryExitRecord &record = entryExitRecord[index];
848 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
851 hb_position_t entry_x, entry_y;
852 (this+record.entryAnchor).get_anchor (context, IN_CURGLYPH (), &entry_x, &entry_y);
856 if (buffer->direction == HB_DIRECTION_RTL)
858 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
859 POSITION (buffer->in_pos)->new_advance = TRUE;
863 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
864 POSITION (last_pos)->new_advance = TRUE;
867 if (lookup_flag & LookupFlag::RightToLeft)
869 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
870 POSITION (last_pos)->y_pos = entry_y - gpi->anchor_y;
874 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
875 POSITION (buffer->in_pos)->y_pos = gpi->anchor_y - entry_y;
879 if (record.exitAnchor)
881 gpi->last = buffer->in_pos;
882 (this+record.exitAnchor).get_anchor (context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
889 inline bool sanitize (SANITIZE_ARG_DEF) {
891 return SANITIZE_THIS2 (coverage, entryExitRecord);
895 USHORT format; /* Format identifier--format = 1 */
897 coverage; /* Offset to Coverage table--from
898 * beginning of subtable */
899 ArrayOf<EntryExitRecord>
900 entryExitRecord; /* Array of EntryExit records--in
901 * Coverage Index order */
903 ASSERT_SIZE (CursivePosFormat1, 6);
907 friend struct PosLookupSubTable;
910 inline bool apply (APPLY_ARG_DEF) const
914 case 1: return u.format1->apply (APPLY_ARG);
915 default:return false;
919 inline bool sanitize (SANITIZE_ARG_DEF) {
921 if (!SANITIZE (u.format)) return false;
923 case 1: return u.format1->sanitize (SANITIZE_ARG);
930 USHORT format; /* Format identifier */
931 CursivePosFormat1 format1[VAR];
936 typedef AnchorMatrix BaseArray; /* base-major--
937 * in order of BaseCoverage Index--,
939 * ordered by class--zero-based. */
941 struct MarkBasePosFormat1
943 friend struct MarkBasePos;
946 inline bool apply (APPLY_ARG_DEF) const
949 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
950 if (HB_LIKELY (mark_index == NOT_COVERED))
953 /* now we search backwards for a non-mark glyph */
954 unsigned int j = buffer->in_pos;
957 if (HB_UNLIKELY (!j))
960 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
963 /* The following assertion is too strong. */
964 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
968 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
969 if (base_index == NOT_COVERED)
972 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
975 inline bool sanitize (SANITIZE_ARG_DEF) {
977 return SANITIZE_SELF () && SANITIZE_THIS2 (markCoverage, baseCoverage) &&
978 SANITIZE_THIS (markArray) && baseArray.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
982 USHORT format; /* Format identifier--format = 1 */
984 markCoverage; /* Offset to MarkCoverage table--from
985 * beginning of MarkBasePos subtable */
987 baseCoverage; /* Offset to BaseCoverage table--from
988 * beginning of MarkBasePos subtable */
989 USHORT classCount; /* Number of classes defined for marks */
991 markArray; /* Offset to MarkArray table--from
992 * beginning of MarkBasePos subtable */
994 baseArray; /* Offset to BaseArray table--from
995 * beginning of MarkBasePos subtable */
997 ASSERT_SIZE (MarkBasePosFormat1, 12);
1001 friend struct PosLookupSubTable;
1004 inline bool apply (APPLY_ARG_DEF) const
1008 case 1: return u.format1->apply (APPLY_ARG);
1009 default:return false;
1013 inline bool sanitize (SANITIZE_ARG_DEF) {
1015 if (!SANITIZE (u.format)) return false;
1017 case 1: return u.format1->sanitize (SANITIZE_ARG);
1018 default:return true;
1024 USHORT format; /* Format identifier */
1025 MarkBasePosFormat1 format1[VAR];
1030 typedef AnchorMatrix LigatureAttach; /* component-major--
1031 * in order of writing direction--,
1033 * ordered by class--zero-based. */
1035 typedef OffsetListOf<LigatureAttach> LigatureArray;
1036 /* Array of LigatureAttach
1038 * LigatureCoverage Index */
1040 struct MarkLigPosFormat1
1042 friend struct MarkLigPos;
1045 inline bool apply (APPLY_ARG_DEF) const
1048 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1049 if (HB_LIKELY (mark_index == NOT_COVERED))
1052 /* now we search backwards for a non-mark glyph */
1053 unsigned int j = buffer->in_pos;
1056 if (HB_UNLIKELY (!j))
1059 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1062 /* The following assertion is too strong. */
1063 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1067 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1068 if (lig_index == NOT_COVERED)
1071 const LigatureArray& lig_array = this+ligatureArray;
1072 const LigatureAttach& lig_attach = lig_array[lig_index];
1074 /* Find component to attach to */
1075 unsigned int comp_count = lig_attach.rows;
1076 if (HB_UNLIKELY (!comp_count))
1078 unsigned int comp_index;
1079 /* We must now check whether the ligature ID of the current mark glyph
1080 * is identical to the ligature ID of the found ligature. If yes, we
1081 * can directly use the component index. If not, we attach the mark
1082 * glyph to the last component of the ligature. */
1083 if (IN_LIGID (j) == IN_LIGID (buffer->in_pos))
1085 comp_index = IN_COMPONENT (buffer->in_pos);
1086 if (comp_index >= comp_count)
1087 comp_index = comp_count - 1;
1090 comp_index = comp_count - 1;
1092 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1095 inline bool sanitize (SANITIZE_ARG_DEF) {
1097 return SANITIZE_SELF () &&
1098 SANITIZE_THIS2 (markCoverage, ligatureCoverage) &&
1099 SANITIZE_THIS (markArray) && ligatureArray.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
1103 USHORT format; /* Format identifier--format = 1 */
1105 markCoverage; /* Offset to Mark Coverage table--from
1106 * beginning of MarkLigPos subtable */
1108 ligatureCoverage; /* Offset to Ligature Coverage
1109 * table--from beginning of MarkLigPos
1111 USHORT classCount; /* Number of defined mark classes */
1113 markArray; /* Offset to MarkArray table--from
1114 * beginning of MarkLigPos subtable */
1115 OffsetTo<LigatureArray>
1116 ligatureArray; /* Offset to LigatureArray table--from
1117 * beginning of MarkLigPos subtable */
1119 ASSERT_SIZE (MarkLigPosFormat1, 12);
1123 friend struct PosLookupSubTable;
1126 inline bool apply (APPLY_ARG_DEF) const
1130 case 1: return u.format1->apply (APPLY_ARG);
1131 default:return false;
1135 inline bool sanitize (SANITIZE_ARG_DEF) {
1137 if (!SANITIZE (u.format)) return false;
1139 case 1: return u.format1->sanitize (SANITIZE_ARG);
1140 default:return true;
1146 USHORT format; /* Format identifier */
1147 MarkLigPosFormat1 format1[VAR];
1152 typedef AnchorMatrix Mark2Array; /* mark2-major--
1153 * in order of Mark2Coverage Index--,
1155 * ordered by class--zero-based. */
1157 struct MarkMarkPosFormat1
1159 friend struct MarkMarkPos;
1162 inline bool apply (APPLY_ARG_DEF) const
1165 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1166 if (HB_LIKELY (mark1_index == NOT_COVERED))
1169 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1170 unsigned int j = buffer->in_pos;
1173 if (HB_UNLIKELY (!j))
1176 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property));
1178 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1181 /* Two marks match only if they belong to the same base, or same component
1182 * of the same ligature. */
1183 if (IN_LIGID (j) != IN_LIGID (buffer->in_pos) ||
1184 IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos))
1187 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1188 if (mark2_index == NOT_COVERED)
1191 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1194 inline bool sanitize (SANITIZE_ARG_DEF) {
1196 return SANITIZE_SELF () && SANITIZE_THIS2 (mark1Coverage, mark2Coverage) &&
1197 SANITIZE_THIS (mark1Array) && mark2Array.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
1201 USHORT format; /* Format identifier--format = 1 */
1203 mark1Coverage; /* Offset to Combining Mark1 Coverage
1204 * table--from beginning of MarkMarkPos
1207 mark2Coverage; /* Offset to Combining Mark2 Coverage
1208 * table--from beginning of MarkMarkPos
1210 USHORT classCount; /* Number of defined mark classes */
1212 mark1Array; /* Offset to Mark1Array table--from
1213 * beginning of MarkMarkPos subtable */
1214 OffsetTo<Mark2Array>
1215 mark2Array; /* Offset to Mark2Array table--from
1216 * beginning of MarkMarkPos subtable */
1218 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1222 friend struct PosLookupSubTable;
1225 inline bool apply (APPLY_ARG_DEF) const
1229 case 1: return u.format1->apply (APPLY_ARG);
1230 default:return false;
1234 inline bool sanitize (SANITIZE_ARG_DEF) {
1236 if (!SANITIZE (u.format)) return false;
1238 case 1: return u.format1->sanitize (SANITIZE_ARG);
1239 default:return true;
1245 USHORT format; /* Format identifier */
1246 MarkMarkPosFormat1 format1[VAR];
1251 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1253 struct ContextPos : Context
1255 friend struct PosLookupSubTable;
1258 inline bool apply (APPLY_ARG_DEF) const
1261 return Context::apply (APPLY_ARG, position_lookup);
1265 struct ChainContextPos : ChainContext
1267 friend struct PosLookupSubTable;
1270 inline bool apply (APPLY_ARG_DEF) const
1273 return ChainContext::apply (APPLY_ARG, position_lookup);
1278 struct ExtensionPos : Extension
1280 friend struct PosLookupSubTable;
1283 inline const struct PosLookupSubTable& get_subtable (void) const
1284 { return CONST_CAST (PosLookupSubTable, Extension::get_subtable (), 0); }
1286 inline bool apply (APPLY_ARG_DEF) const;
1288 inline bool sanitize (SANITIZE_ARG_DEF);
1298 struct PosLookupSubTable
1300 friend struct PosLookup;
1314 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1317 switch (lookup_type) {
1318 case Single: return u.single->apply (APPLY_ARG);
1319 case Pair: return u.pair->apply (APPLY_ARG);
1320 case Cursive: return u.cursive->apply (APPLY_ARG);
1321 case MarkBase: return u.markBase->apply (APPLY_ARG);
1322 case MarkLig: return u.markLig->apply (APPLY_ARG);
1323 case MarkMark: return u.markMark->apply (APPLY_ARG);
1324 case Context: return u.context->apply (APPLY_ARG);
1325 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1326 case Extension: return u.extension->apply (APPLY_ARG);
1327 default:return false;
1331 inline bool sanitize (SANITIZE_ARG_DEF) {
1333 if (!SANITIZE (u.format)) return false;
1335 case Single: return u.single->sanitize (SANITIZE_ARG);
1336 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1337 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1338 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1339 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1340 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1341 case Context: return u.context->sanitize (SANITIZE_ARG);
1342 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1343 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1344 default:return true;
1351 SinglePos single[VAR];
1353 CursivePos cursive[VAR];
1354 MarkBasePos markBase[VAR];
1355 MarkLigPos markLig[VAR];
1356 MarkMarkPos markMark[VAR];
1357 ContextPos context[VAR];
1358 ChainContextPos chainContext[VAR];
1359 ExtensionPos extension[VAR];
1364 struct PosLookup : Lookup
1366 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1367 { return (const PosLookupSubTable&) Lookup::get_subtable (i); }
1369 /* Like get_type(), but looks through extension lookups.
1370 * Never returns Extension */
1371 inline unsigned int get_effective_type (void) const
1373 unsigned int type = get_type ();
1375 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
1377 unsigned int count = get_subtable_count ();
1378 type = get_subtable(0).u.extension->get_type ();
1379 /* The spec says all subtables should have the same type.
1380 * This is specially important if one has a reverse type! */
1381 for (unsigned int i = 1; i < count; i++)
1382 if (get_subtable(i).u.extension->get_type () != type)
1389 inline bool apply_once (hb_ot_layout_context_t *context,
1390 hb_buffer_t *buffer,
1391 unsigned int context_length,
1392 unsigned int nesting_level_left) const
1394 unsigned int lookup_type = get_type ();
1395 unsigned int lookup_flag = get_flag ();
1396 unsigned int property;
1398 if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
1401 for (unsigned int i = 0; i < get_subtable_count (); i++)
1402 if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
1408 inline bool apply_string (hb_ot_layout_context_t *context,
1409 hb_buffer_t *buffer,
1410 hb_mask_t mask) const
1414 if (HB_UNLIKELY (!buffer->in_length))
1417 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1420 while (buffer->in_pos < buffer->in_length)
1423 if (~IN_MASK (buffer->in_pos) & mask)
1425 done = apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1431 /* Contrary to properties defined in GDEF, user-defined properties
1432 will always stop a possible cursive positioning. */
1433 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1443 inline bool sanitize (SANITIZE_ARG_DEF) {
1445 if (!Lookup::sanitize (SANITIZE_ARG)) return false;
1446 OffsetArrayOf<PosLookupSubTable> &list = (OffsetArrayOf<PosLookupSubTable> &) subTable;
1447 return SANITIZE_THIS (list);
1451 typedef OffsetListOf<PosLookup> PosLookupList;
1452 ASSERT_SIZE (PosLookupList, 2);
1458 struct GPOS : GSUBGPOS
1460 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1462 static inline const GPOS& get_for_data (const char *data)
1463 { return (const GPOS&) GSUBGPOS::get_for_data (data); }
1465 inline const PosLookup& get_lookup (unsigned int i) const
1466 { return (const PosLookup&) GSUBGPOS::get_lookup (i); }
1468 inline bool position_lookup (hb_ot_layout_context_t *context,
1469 hb_buffer_t *buffer,
1470 unsigned int lookup_index,
1471 hb_mask_t mask) const
1472 { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
1474 inline bool sanitize (SANITIZE_ARG_DEF) {
1476 if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
1477 OffsetTo<PosLookupList> &list = CAST(OffsetTo<PosLookupList>, lookupList, 0);
1478 return SANITIZE_THIS (list);
1481 ASSERT_SIZE (GPOS, 10);
1484 /* Out-of-class implementation for methods recursing */
1486 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1489 unsigned int lookup_type = get_type ();
1491 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
1494 return get_subtable ().apply (APPLY_ARG, lookup_type);
1497 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1500 return Extension::sanitize (SANITIZE_ARG) &&
1501 (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
1502 get_type () == PosLookupSubTable::Extension ||
1503 DECONST_CAST (PosLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
1506 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1508 const GPOS &gpos = *(context->face->ot_layout.gpos);
1509 const PosLookup &l = gpos.get_lookup (lookup_index);
1511 if (HB_UNLIKELY (nesting_level_left == 0))
1513 nesting_level_left--;
1515 if (HB_UNLIKELY (context_length < 1))
1518 return l.apply_once (context, buffer, context_length, nesting_level_left);
1522 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */