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 */
37 typedef Value ValueRecord[];
39 struct ValueFormat : USHORT
43 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
44 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
45 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
46 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
47 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
48 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
49 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
50 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
51 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
52 reserved = 0xF000, /* For future use */
55 inline unsigned int get_len () const
56 { return _hb_popcount32 ((unsigned int) *this); }
57 inline unsigned int get_size () const
58 { return get_len () * sizeof (Value); }
60 void apply_value (hb_ot_layout_context_t *context,
63 hb_internal_glyph_position_t *glyph_pos) const
65 unsigned int x_ppem, y_ppem;
66 hb_16dot16_t x_scale, y_scale;
67 unsigned int format = *this;
72 /* All fields are options. Only those available advance the value
76 SHORT xPlacement; /* Horizontal adjustment for
77 * placement--in design units */
78 SHORT yPlacement; /* Vertical adjustment for
79 * placement--in design units */
80 SHORT xAdvance; /* Horizontal adjustment for
81 * advance--in design units (only used
82 * for horizontal writing) */
83 SHORT yAdvance; /* Vertical adjustment for advance--in
84 * design units (only used for vertical
86 Offset xPlaDevice; /* Offset to Device table for
87 * horizontal placement--measured from
88 * beginning of PosTable (may be NULL) */
89 Offset yPlaDevice; /* Offset to Device table for vertical
90 * placement--measured from beginning
91 * of PosTable (may be NULL) */
92 Offset xAdvDevice; /* Offset to Device table for
93 * horizontal advance--measured from
94 * beginning of PosTable (may be NULL) */
95 Offset yAdvDevice; /* Offset to Device table for vertical
96 * advance--measured from beginning of
97 * PosTable (may be NULL) */
101 x_scale = context->font->x_scale;
102 y_scale = context->font->y_scale;
103 /* design units -> fractional pixel */
104 if (format & xPlacement)
105 glyph_pos->x_pos += x_scale * *(SHORT*)values++ / 0x10000;
106 if (format & yPlacement)
107 glyph_pos->y_pos += y_scale * *(SHORT*)values++ / 0x10000;
108 if (format & xAdvance)
109 glyph_pos->x_advance += x_scale * *(SHORT*)values++ / 0x10000;
110 if (format & yAdvance)
111 glyph_pos->y_advance += y_scale * *(SHORT*)values++ / 0x10000;
113 x_ppem = context->font->x_ppem;
114 y_ppem = context->font->y_ppem;
115 /* pixel -> fractional pixel */
116 if (format & xPlaDevice) {
118 glyph_pos->x_pos += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
122 if (format & yPlaDevice) {
124 glyph_pos->y_pos += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
128 if (format & xAdvDevice) {
130 glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 6;
134 if (format & yAdvDevice) {
136 glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 6;
142 ASSERT_SIZE (ValueFormat, 2);
147 friend struct Anchor;
150 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
151 hb_position_t *x, hb_position_t *y) const
153 *x = context->font->x_scale * xCoordinate / 0x10000;
154 *y = context->font->y_scale * yCoordinate / 0x10000;
157 inline bool sanitize (SANITIZE_ARG_DEF) {
159 return SANITIZE_SELF ();
163 USHORT format; /* Format identifier--format = 1 */
164 SHORT xCoordinate; /* Horizontal value--in design units */
165 SHORT yCoordinate; /* Vertical value--in design units */
167 ASSERT_SIZE (AnchorFormat1, 6);
171 friend struct Anchor;
174 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
175 hb_position_t *x, hb_position_t *y) const
178 *x = context->font->x_scale * xCoordinate / 0x10000;
179 *y = context->font->y_scale * yCoordinate / 0x10000;
182 inline bool sanitize (SANITIZE_ARG_DEF) {
184 return SANITIZE_SELF ();
188 USHORT format; /* Format identifier--format = 2 */
189 SHORT xCoordinate; /* Horizontal value--in design units */
190 SHORT yCoordinate; /* Vertical value--in design units */
191 USHORT anchorPoint; /* Index to glyph contour point */
193 ASSERT_SIZE (AnchorFormat2, 8);
197 friend struct Anchor;
200 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
201 hb_position_t *x, hb_position_t *y) const
203 *x = context->font->x_scale * xCoordinate / 0x10000;
204 *y = context->font->y_scale * yCoordinate / 0x10000;
206 if (context->font->x_ppem)
207 *x += (this+xDeviceTable).get_delta (context->font->x_ppem) << 6;
208 if (context->font->y_ppem)
209 *y += (this+yDeviceTable).get_delta (context->font->y_ppem) << 6;
212 inline bool sanitize (SANITIZE_ARG_DEF) {
214 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
218 USHORT format; /* Format identifier--format = 3 */
219 SHORT xCoordinate; /* Horizontal value--in design units */
220 SHORT yCoordinate; /* Vertical value--in design units */
222 xDeviceTable; /* Offset to Device table for X
223 * coordinate-- from beginning of
224 * Anchor table (may be NULL) */
226 yDeviceTable; /* Offset to Device table for Y
227 * coordinate-- from beginning of
228 * Anchor table (may be NULL) */
230 ASSERT_SIZE (AnchorFormat3, 10);
234 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
235 hb_position_t *x, hb_position_t *y) const
239 case 1: u.format1->get_anchor (context, glyph_id, x, y); return;
240 case 2: u.format2->get_anchor (context, glyph_id, x, y); return;
241 case 3: u.format3->get_anchor (context, glyph_id, x, y); return;
246 inline bool sanitize (SANITIZE_ARG_DEF) {
248 if (!SANITIZE (u.format)) return false;
250 case 1: return u.format1->sanitize (SANITIZE_ARG);
251 case 2: return u.format2->sanitize (SANITIZE_ARG);
252 case 3: return u.format3->sanitize (SANITIZE_ARG);
259 USHORT format; /* Format identifier */
260 AnchorFormat1 format1[];
261 AnchorFormat2 format2[];
262 AnchorFormat3 format3[];
265 ASSERT_SIZE (Anchor, 2);
270 friend struct MarkArray;
272 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
274 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
278 USHORT klass; /* Class defined for this mark */
280 markAnchor; /* Offset to Anchor table--from
281 * beginning of MarkArray table */
283 ASSERT_SIZE (MarkRecord, 4);
287 inline unsigned int get_class (unsigned int index) const { return markRecord[index].klass; }
288 inline const Anchor& get_anchor (unsigned int index) const { return this+markRecord[index].markAnchor; }
290 inline bool sanitize (SANITIZE_ARG_DEF) {
292 return SANITIZE_THIS (markRecord);
297 markRecord; /* Array of MarkRecords--in Coverage order */
299 ASSERT_SIZE (MarkArray, 2);
304 struct SinglePosFormat1
306 friend struct SinglePos;
309 inline bool apply (APPLY_ARG_DEF) const
311 unsigned int index = (this+coverage) (IN_CURGLYPH ());
312 if (HB_LIKELY (index == NOT_COVERED))
315 valueFormat.apply_value (context, CONST_CHARP(this), values, CURPOSITION ());
321 inline bool sanitize (SANITIZE_ARG_DEF) {
323 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
324 SANITIZE_MEM (values, valueFormat.get_size ());
328 USHORT format; /* Format identifier--format = 1 */
330 coverage; /* Offset to Coverage table--from
331 * beginning of subtable */
332 ValueFormat valueFormat; /* Defines the types of data in the
334 ValueRecord values; /* Defines positioning
335 * value(s)--applied to all glyphs in
336 * the Coverage table */
338 ASSERT_SIZE (SinglePosFormat1, 6);
340 struct SinglePosFormat2
342 friend struct SinglePos;
345 inline bool apply (APPLY_ARG_DEF) const
347 unsigned int index = (this+coverage) (IN_CURGLYPH ());
348 if (HB_LIKELY (index == NOT_COVERED))
351 if (HB_LIKELY (index >= valueCount))
354 valueFormat.apply_value (context, CONST_CHARP(this),
355 values + index * valueFormat.get_len (),
362 inline bool sanitize (SANITIZE_ARG_DEF) {
364 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
365 SANITIZE_MEM (values, valueFormat.get_size () * valueCount);
369 USHORT format; /* Format identifier--format = 2 */
371 coverage; /* Offset to Coverage table--from
372 * beginning of subtable */
373 ValueFormat valueFormat; /* Defines the types of data in the
375 USHORT valueCount; /* Number of ValueRecords */
376 ValueRecord values; /* Array of ValueRecords--positioning
377 * values applied to glyphs */
379 ASSERT_SIZE (SinglePosFormat2, 8);
383 friend struct PosLookupSubTable;
386 inline bool apply (APPLY_ARG_DEF) const
389 case 1: return u.format1->apply (APPLY_ARG);
390 case 2: return u.format2->apply (APPLY_ARG);
391 default:return false;
395 inline bool sanitize (SANITIZE_ARG_DEF) {
397 if (!SANITIZE (u.format)) return false;
399 case 1: return u.format1->sanitize (SANITIZE_ARG);
400 case 2: return u.format2->sanitize (SANITIZE_ARG);
407 USHORT format; /* Format identifier */
408 SinglePosFormat1 format1[];
409 SinglePosFormat2 format2[];
412 ASSERT_SIZE (SinglePos, 2);
415 struct PairValueRecord
417 friend struct PairPosFormat1;
420 GlyphID secondGlyph; /* GlyphID of second glyph in the
421 * pair--first glyph is listed in the
423 ValueRecord values; /* Positioning data for the first glyph
424 * followed by for second glyph */
426 ASSERT_SIZE (PairValueRecord, 2);
430 friend struct PairPosFormat1;
432 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
434 if (!SANITIZE_SELF ()) return false;
435 unsigned int count = (1 + format_len) * len;
436 return SANITIZE_MEM (array, sizeof (array[0]) * count);
440 USHORT len; /* Number of PairValueRecords */
442 array[]; /* Array of PairValueRecords--ordered
443 * by GlyphID of the second glyph */
445 ASSERT_SIZE (PairSet, 2);
447 struct PairPosFormat1
449 friend struct PairPos;
452 inline bool apply (APPLY_ARG_DEF) const
454 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
455 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
458 unsigned int index = (this+coverage) (IN_CURGLYPH ());
459 if (HB_LIKELY (index == NOT_COVERED))
462 unsigned int j = buffer->in_pos + 1;
463 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
465 if (HB_UNLIKELY (j == end))
470 const PairSet &pair_set = this+pairSet[index];
472 unsigned int len1 = valueFormat1.get_len ();
473 unsigned int len2 = valueFormat2.get_len ();
474 unsigned int record_len = 1 + len1 + len2;
476 unsigned int count = pair_set.len;
477 const PairValueRecord *record = pair_set.array;
478 for (unsigned int i = 0; i < count; i++)
480 if (IN_GLYPH (j) == record->secondGlyph)
482 valueFormat1.apply_value (context, CONST_CHARP(this), record->values, CURPOSITION ());
483 valueFormat2.apply_value (context, CONST_CHARP(this), record->values + len1, POSITION (j));
489 record += record_len;
495 inline bool sanitize (SANITIZE_ARG_DEF) {
497 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
498 pairSet.sanitize (SANITIZE_ARG, CONST_CHARP(this),
499 valueFormat1.get_len () + valueFormat2.get_len ());
503 USHORT format; /* Format identifier--format = 1 */
505 coverage; /* Offset to Coverage table--from
506 * beginning of subtable */
507 ValueFormat valueFormat1; /* Defines the types of data in
508 * ValueRecord1--for the first glyph
509 * in the pair--may be zero (0) */
510 ValueFormat valueFormat2; /* Defines the types of data in
511 * ValueRecord2--for the second glyph
512 * in the pair--may be zero (0) */
513 OffsetArrayOf<PairSet>
514 pairSet; /* Array of PairSet tables
515 * ordered by Coverage Index */
517 ASSERT_SIZE (PairPosFormat1, 10);
519 struct PairPosFormat2
521 friend struct PairPos;
524 inline bool apply (APPLY_ARG_DEF) const
526 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
527 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
530 unsigned int index = (this+coverage) (IN_CURGLYPH ());
531 if (HB_LIKELY (index == NOT_COVERED))
534 unsigned int j = buffer->in_pos + 1;
535 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
537 if (HB_UNLIKELY (j == end))
542 unsigned int len1 = valueFormat1.get_len ();
543 unsigned int len2 = valueFormat2.get_len ();
544 unsigned int record_len = len1 + len2;
546 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
547 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
548 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
551 const Value *v = values + record_len * (klass1 * class2Count + klass2);
552 valueFormat1.apply_value (context, CONST_CHARP(this), v, CURPOSITION ());
553 valueFormat2.apply_value (context, CONST_CHARP(this), v + len1, POSITION (j));
562 inline bool sanitize (SANITIZE_ARG_DEF) {
564 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
565 SANITIZE_THIS2 (classDef1, classDef2) &&
566 SANITIZE_MEM (values,
567 (valueFormat1.get_size () + valueFormat2.get_size ()) *
568 class1Count * class2Count);
572 USHORT format; /* Format identifier--format = 2 */
574 coverage; /* Offset to Coverage table--from
575 * beginning of subtable */
576 ValueFormat valueFormat1; /* ValueRecord definition--for the
577 * first glyph of the pair--may be zero
579 ValueFormat valueFormat2; /* ValueRecord definition--for the
580 * second glyph of the pair--may be
583 classDef1; /* Offset to ClassDef table--from
584 * beginning of PairPos subtable--for
585 * the first glyph of the pair */
587 classDef2; /* Offset to ClassDef table--from
588 * beginning of PairPos subtable--for
589 * the second glyph of the pair */
590 USHORT class1Count; /* Number of classes in ClassDef1
591 * table--includes Class0 */
592 USHORT class2Count; /* Number of classes in ClassDef2
593 * table--includes Class0 */
594 ValueRecord values; /* Matrix of value pairs:
595 * class1-major, class2-minor,
596 * Each entry has value1 and value2 */
598 ASSERT_SIZE (PairPosFormat2, 16);
602 friend struct PosLookupSubTable;
605 inline bool apply (APPLY_ARG_DEF) const
608 case 1: return u.format1->apply (APPLY_ARG);
609 case 2: return u.format2->apply (APPLY_ARG);
610 default:return false;
614 inline bool sanitize (SANITIZE_ARG_DEF) {
616 if (!SANITIZE (u.format)) return false;
618 case 1: return u.format1->sanitize (SANITIZE_ARG);
619 case 2: return u.format2->sanitize (SANITIZE_ARG);
626 USHORT format; /* Format identifier */
627 PairPosFormat1 format1[];
628 PairPosFormat2 format2[];
631 ASSERT_SIZE (PairPos, 2);
634 struct EntryExitRecord
636 inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
638 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
642 entryAnchor; /* Offset to EntryAnchor table--from
643 * beginning of CursivePos
644 * subtable--may be NULL */
646 exitAnchor; /* Offset to ExitAnchor table--from
647 * beginning of CursivePos
648 * subtable--may be NULL */
650 ASSERT_SIZE (EntryExitRecord, 4);
652 struct CursivePosFormat1
654 friend struct CursivePos;
657 inline bool apply (APPLY_ARG_DEF) const
659 /* Now comes the messiest part of the whole OpenType
660 specification. At first glance, cursive connections seem easy
661 to understand, but there are pitfalls! The reason is that
662 the specs don't mention how to compute the advance values
663 resp. glyph offsets. I was told it would be an omission, to
664 be fixed in the next OpenType version... Again many thanks to
665 Andrei Burago <andreib@microsoft.com> for clarifications.
667 Consider the following example:
680 glyph1: advance width = 12
683 glyph2: advance width = 11
686 LSB is 1 for both glyphs (so the boxes drawn above are glyph
687 bboxes). Writing direction is R2L; `0' denotes the glyph's
690 Now the surprising part: The advance width of the *left* glyph
691 (resp. of the *bottom* glyph) will be modified, no matter
692 whether the writing direction is L2R or R2L (resp. T2B or
693 B2T)! This assymetry is caused by the fact that the glyph's
694 coordinate origin is always the lower left corner for all
697 Continuing the above example, we can compute the new
698 (horizontal) advance width of glyph2 as
702 and the new vertical offset of glyph2 as
707 Vertical writing direction is far more complicated:
709 a) Assuming that we recompute the advance height of the lower glyph:
716 yadv2 | 0+--+------+ -- BSB1 --
719 BSB2 -- 0+--------+ --
722 glyph1: advance height = 6
725 glyph2: advance height = 7
728 TSB is 1 for both glyphs; writing direction is T2B.
731 BSB1 = yadv1 - (TSB1 + ymax1)
732 BSB2 = yadv2 - (TSB2 + ymax2)
735 vertical advance width of glyph2
736 = y_offset + BSB2 - BSB1
737 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
738 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
739 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
742 b) Assuming that we recompute the advance height of the upper glyph:
747 TSB2 -- +-----+--+ 1 | yadv1 ymax1
749 yadv2 | 0+--+------+ -- --
750 ymax2 | 2 | -- y_offset
755 glyph1: advance height = 6
758 glyph2: advance height = 7
761 TSB is 1 for both glyphs; writing direction is T2B.
765 vertical advance width of glyph2
766 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
767 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
770 Comparing a) with b) shows that b) is easier to compute. I'll wait
771 for a reply from Andrei to see what should really be implemented...
773 Since horizontal advance widths or vertical advance heights
774 can be used alone but not together, no ambiguity occurs. */
776 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->info.gpos;
777 hb_codepoint_t last_pos = gpi->last;
778 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
780 /* We don't handle mark glyphs here. */
781 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
784 unsigned int index = (this+coverage) (IN_CURGLYPH ());
785 if (HB_LIKELY (index == NOT_COVERED))
788 const EntryExitRecord &record = entryExitRecord[index];
790 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
793 hb_position_t entry_x, entry_y;
794 (this+record.entryAnchor).get_anchor (context, IN_CURGLYPH (), &entry_x, &entry_y);
798 if (buffer->direction == HB_DIRECTION_RTL)
800 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
801 POSITION (buffer->in_pos)->new_advance = TRUE;
805 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
806 POSITION (last_pos)->new_advance = TRUE;
809 if (lookup_flag & LookupFlag::RightToLeft)
811 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
812 POSITION (last_pos)->y_pos = entry_y - gpi->anchor_y;
816 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
817 POSITION (buffer->in_pos)->y_pos = gpi->anchor_y - entry_y;
821 if (record.exitAnchor)
823 gpi->last = buffer->in_pos;
824 (this+record.exitAnchor).get_anchor (context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
831 inline bool sanitize (SANITIZE_ARG_DEF) {
833 return SANITIZE_THIS2 (coverage, entryExitRecord);
837 USHORT format; /* Format identifier--format = 1 */
839 coverage; /* Offset to Coverage table--from
840 * beginning of subtable */
841 ArrayOf<EntryExitRecord>
842 entryExitRecord; /* Array of EntryExit records--in
843 * Coverage Index order */
845 ASSERT_SIZE (CursivePosFormat1, 6);
849 friend struct PosLookupSubTable;
852 inline bool apply (APPLY_ARG_DEF) const
855 case 1: return u.format1->apply (APPLY_ARG);
856 default:return false;
860 inline bool sanitize (SANITIZE_ARG_DEF) {
862 if (!SANITIZE (u.format)) return false;
864 case 1: return u.format1->sanitize (SANITIZE_ARG);
871 USHORT format; /* Format identifier */
872 CursivePosFormat1 format1[];
875 ASSERT_SIZE (CursivePos, 2);
880 friend struct MarkBasePosFormat1;
882 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
884 if (!SANITIZE_SELF ()) return false;
885 unsigned int count = cols * len;
886 if (!SANITIZE_MEM (matrix, sizeof (matrix[0]) * count)) return false;
887 for (unsigned int i = 0; i < count; i++)
888 if (!SANITIZE_THIS (matrix[i])) return false;
893 USHORT len; /* Number of rows */
895 matrix[]; /* Matrix of offsets to Anchor tables--
896 * from beginning of BaseArray table--
897 * base-major--in order of
898 * BaseCoverage Index--, mark-minor--
899 * ordered by class--zero-based. */
901 ASSERT_SIZE (BaseArray, 2);
903 struct MarkBasePosFormat1
905 friend struct MarkBasePos;
908 inline bool apply (APPLY_ARG_DEF) const
910 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
911 if (HB_LIKELY (mark_index == NOT_COVERED))
914 /* now we search backwards for a non-mark glyph */
915 unsigned int count = buffer->in_pos;
916 unsigned int i = 1, j = count - 1;
917 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property))
919 if (HB_UNLIKELY (i == count))
924 /* The following assertion is too strong. */
925 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
929 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
930 if (base_index == NOT_COVERED)
933 const MarkArray& mark_array = this+markArray;
934 const BaseArray& base_array = this+baseArray;
936 unsigned int mark_class = mark_array.get_class (mark_index);
937 const Anchor& mark_anchor = mark_array.get_anchor (mark_index);
939 if (HB_UNLIKELY (mark_class >= classCount || base_index >= base_array.len))
942 hb_position_t mark_x, mark_y, base_x, base_y;
944 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
945 unsigned int index = base_index * classCount + mark_class;
946 (&base_array+base_array.matrix[index]).get_anchor (context, IN_GLYPH (j), &base_x, &base_y);
948 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
949 o->x_pos = base_x - mark_x;
950 o->y_pos = base_y - mark_y;
959 inline bool sanitize (SANITIZE_ARG_DEF) {
961 return SANITIZE_SELF () && SANITIZE_THIS2 (markCoverage, baseCoverage) &&
962 SANITIZE_THIS (markArray) && baseArray.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
966 USHORT format; /* Format identifier--format = 1 */
968 markCoverage; /* Offset to MarkCoverage table--from
969 * beginning of MarkBasePos subtable */
971 baseCoverage; /* Offset to BaseCoverage table--from
972 * beginning of MarkBasePos subtable */
973 USHORT classCount; /* Number of classes defined for marks */
975 markArray; /* Offset to MarkArray table--from
976 * beginning of MarkBasePos subtable */
978 baseArray; /* Offset to BaseArray table--from
979 * beginning of MarkBasePos subtable */
981 ASSERT_SIZE (MarkBasePosFormat1, 12);
985 friend struct PosLookupSubTable;
988 inline bool apply (APPLY_ARG_DEF) const
991 case 1: return u.format1->apply (APPLY_ARG);
992 default:return false;
996 inline bool sanitize (SANITIZE_ARG_DEF) {
998 if (!SANITIZE (u.format)) return false;
1000 case 1: return u.format1->sanitize (SANITIZE_ARG);
1001 default:return true;
1007 USHORT format; /* Format identifier */
1008 MarkBasePosFormat1 format1[];
1011 ASSERT_SIZE (MarkBasePos, 2);
1014 struct LigatureAttach
1016 friend struct MarkLigPosFormat1;
1019 USHORT len; /* Number of ComponentRecords in this
1020 * ligature, ie. number of rows */
1022 matrix[]; /* Matrix of offsets to Anchor tables--
1023 * from beginning of LigatureAttach table--
1024 * component-major--in order of
1025 * writing direction--, mark-minor--
1026 * ordered by class--zero-based. */
1028 ASSERT_SIZE (LigatureAttach, 2);
1030 typedef OffsetArrayOf<LigatureAttach> LigatureArray;
1031 /* Array of LigatureAttach
1033 * LigatureCoverage Index */
1034 ASSERT_SIZE (LigatureArray, 2);
1036 struct MarkLigPosFormat1
1038 friend struct MarkLigPos;
1041 inline bool apply (APPLY_ARG_DEF) const
1043 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1044 if (HB_LIKELY (mark_index == NOT_COVERED))
1047 /* now we search backwards for a non-mark glyph */
1048 unsigned int count = buffer->in_pos;
1049 unsigned int i = 1, j = count - 1;
1050 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property))
1052 if (HB_UNLIKELY (i == count))
1057 /* The following assertion is too strong. */
1058 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1062 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1063 if (lig_index == NOT_COVERED)
1066 const MarkArray& mark_array = this+markArray;
1067 const LigatureArray& lig_array = this+ligatureArray;
1069 unsigned int mark_class = mark_array.get_class (mark_index);
1070 const Anchor& mark_anchor = mark_array.get_anchor (mark_index);
1072 if (HB_UNLIKELY (mark_class >= classCount || lig_index >= lig_array.len))
1075 const LigatureAttach& lig_attach = &lig_array+lig_array[lig_index];
1076 count = lig_attach.len;
1077 if (HB_UNLIKELY (!count))
1080 unsigned int comp_index;
1081 /* We must now check whether the ligature ID of the current mark glyph
1082 * is identical to the ligature ID of the found ligature. If yes, we
1083 * can directly use the component index. If not, we attach the mark
1084 * glyph to the last component of the ligature. */
1085 if (IN_LIGID (j) == IN_LIGID (buffer->in_pos))
1087 comp_index = IN_COMPONENT (buffer->in_pos);
1088 if (comp_index >= count)
1089 comp_index = count - 1;
1092 comp_index = count - 1;
1094 hb_position_t mark_x, mark_y, lig_x, lig_y;
1096 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
1097 unsigned int index = comp_index * classCount + mark_class;
1098 (&lig_attach+lig_attach.matrix[index]).get_anchor (context, IN_GLYPH (j), &lig_x, &lig_y);
1100 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
1101 o->x_pos = lig_x - mark_x;
1102 o->y_pos = lig_y - mark_y;
1111 inline bool sanitize (SANITIZE_ARG_DEF) {
1113 return SANITIZE_SELF () &&
1114 SANITIZE_THIS2 (markCoverage, ligatureCoverage) &&
1115 SANITIZE_THIS2 (markArray, ligatureArray);
1119 USHORT format; /* Format identifier--format = 1 */
1121 markCoverage; /* Offset to Mark Coverage table--from
1122 * beginning of MarkLigPos subtable */
1124 ligatureCoverage; /* Offset to Ligature Coverage
1125 * table--from beginning of MarkLigPos
1127 USHORT classCount; /* Number of defined mark classes */
1129 markArray; /* Offset to MarkArray table--from
1130 * beginning of MarkLigPos subtable */
1131 OffsetTo<LigatureArray>
1132 ligatureArray; /* Offset to LigatureArray table--from
1133 * beginning of MarkLigPos subtable */
1135 ASSERT_SIZE (MarkLigPosFormat1, 12);
1139 friend struct PosLookupSubTable;
1142 inline bool apply (APPLY_ARG_DEF) const
1145 case 1: return u.format1->apply (APPLY_ARG);
1146 default:return false;
1150 inline bool sanitize (SANITIZE_ARG_DEF) {
1152 if (!SANITIZE (u.format)) return false;
1154 case 1: return u.format1->sanitize (SANITIZE_ARG);
1155 default:return true;
1161 USHORT format; /* Format identifier */
1162 MarkLigPosFormat1 format1[];
1165 ASSERT_SIZE (MarkLigPos, 2);
1170 friend struct MarkMarkPosFormat1;
1172 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
1174 if (!SANITIZE_SELF ()) return false;
1175 unsigned int count = cols * len;
1176 if (!SANITIZE_MEM (matrix, sizeof (matrix[0]) * count)) return false;
1177 for (unsigned int i = 0; i < count; i++)
1178 if (!SANITIZE_THIS (matrix[i])) return false;
1183 USHORT len; /* Number of rows */
1185 matrix[]; /* Matrix of offsets to Anchor tables--
1186 * from beginning of Mark2Array table--
1187 * mark2-major--in order of
1188 * Mark2Coverage Index--, mark1-minor--
1189 * ordered by class--zero-based. */
1191 ASSERT_SIZE (Mark2Array, 2);
1193 struct MarkMarkPosFormat1
1195 friend struct MarkMarkPos;
1198 inline bool apply (APPLY_ARG_DEF) const
1200 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1201 if (HB_LIKELY (mark1_index == NOT_COVERED))
1204 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1205 unsigned int count = buffer->in_pos;
1206 unsigned int i = 1, j = count - 1;
1207 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property))
1209 if (HB_UNLIKELY (i == count))
1213 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1216 /* Two marks match only if they belong to the same base, or same component
1217 * of the same ligature. */
1218 if (IN_LIGID (j) != IN_LIGID (buffer->in_pos) ||
1219 IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos))
1222 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1223 if (mark2_index == NOT_COVERED)
1226 const MarkArray& mark1_array = this+mark1Array;
1227 const Mark2Array& mark2_array = this+mark2Array;
1229 unsigned int mark1_class = mark1_array.get_class (mark1_index);
1230 const Anchor& mark1_anchor = mark1_array.get_anchor (mark1_index);
1232 if (HB_UNLIKELY (mark1_class >= classCount || mark2_index >= mark2_array.len))
1235 hb_position_t mark1_x, mark1_y, mark2_x, mark2_y;
1237 mark1_anchor.get_anchor (context, IN_CURGLYPH (), &mark1_x, &mark1_y);
1238 unsigned int index = mark2_index * classCount + mark1_class;
1239 (&mark2_array+mark2_array.matrix[index]).get_anchor (context, IN_GLYPH (j), &mark2_x, &mark2_y);
1241 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
1242 o->x_pos = mark2_x - mark1_x;
1243 o->y_pos = mark2_y - mark1_y;
1252 inline bool sanitize (SANITIZE_ARG_DEF) {
1254 return SANITIZE_SELF () && SANITIZE_THIS2 (mark1Coverage, mark2Coverage) &&
1255 SANITIZE_THIS (mark1Array) && mark2Array.sanitize (SANITIZE_ARG, CONST_CHARP(this), classCount);
1259 USHORT format; /* Format identifier--format = 1 */
1261 mark1Coverage; /* Offset to Combining Mark1 Coverage
1262 * table--from beginning of MarkMarkPos
1265 mark2Coverage; /* Offset to Combining Mark2 Coverage
1266 * table--from beginning of MarkMarkPos
1268 USHORT classCount; /* Number of defined mark classes */
1270 mark1Array; /* Offset to Mark1Array table--from
1271 * beginning of MarkMarkPos subtable */
1272 OffsetTo<Mark2Array>
1273 mark2Array; /* Offset to Mark2Array table--from
1274 * beginning of MarkMarkPos subtable */
1276 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1280 friend struct PosLookupSubTable;
1283 inline bool apply (APPLY_ARG_DEF) const
1286 case 1: return u.format1->apply (APPLY_ARG);
1287 default:return false;
1291 inline bool sanitize (SANITIZE_ARG_DEF) {
1293 if (!SANITIZE (u.format)) return false;
1295 case 1: return u.format1->sanitize (SANITIZE_ARG);
1296 default:return true;
1302 USHORT format; /* Format identifier */
1303 MarkMarkPosFormat1 format1[];
1306 ASSERT_SIZE (MarkMarkPos, 2);
1309 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1311 struct ContextPos : Context
1313 friend struct PosLookupSubTable;
1316 inline bool apply (APPLY_ARG_DEF) const
1317 { return Context::apply (APPLY_ARG, position_lookup); }
1319 ASSERT_SIZE (ContextPos, 2);
1321 struct ChainContextPos : ChainContext
1323 friend struct PosLookupSubTable;
1326 inline bool apply (APPLY_ARG_DEF) const
1327 { return ChainContext::apply (APPLY_ARG, position_lookup); }
1329 ASSERT_SIZE (ChainContextPos, 2);
1332 struct ExtensionPos : Extension
1334 friend struct PosLookupSubTable;
1337 inline const struct PosLookupSubTable& get_subtable (void) const
1338 { return CONST_CAST (PosLookupSubTable, Extension::get_subtable (), 0); }
1340 inline bool apply (APPLY_ARG_DEF) const;
1342 inline bool sanitize (SANITIZE_ARG_DEF);
1344 ASSERT_SIZE (ExtensionPos, 2);
1353 struct PosLookupSubTable
1355 friend struct PosLookup;
1369 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1371 switch (lookup_type) {
1372 case Single: return u.single->apply (APPLY_ARG);
1373 case Pair: return u.pair->apply (APPLY_ARG);
1374 case Cursive: return u.cursive->apply (APPLY_ARG);
1375 case MarkBase: return u.markBase->apply (APPLY_ARG);
1376 case MarkLig: return u.markLig->apply (APPLY_ARG);
1377 case MarkMark: return u.markMark->apply (APPLY_ARG);
1378 case Context: return u.context->apply (APPLY_ARG);
1379 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1380 case Extension: return u.extension->apply (APPLY_ARG);
1381 default:return false;
1385 inline bool sanitize (SANITIZE_ARG_DEF) {
1387 if (!SANITIZE (u.format)) return false;
1389 case Single: return u.single->sanitize (SANITIZE_ARG);
1390 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1391 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1392 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1393 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1394 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1395 case Context: return u.context->sanitize (SANITIZE_ARG);
1396 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1397 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1398 default:return true;
1407 CursivePos cursive[];
1408 MarkBasePos markBase[];
1409 MarkLigPos markLig[];
1410 MarkMarkPos markMark[];
1411 ContextPos context[];
1412 ChainContextPos chainContext[];
1413 ExtensionPos extension[];
1416 ASSERT_SIZE (PosLookupSubTable, 2);
1419 struct PosLookup : Lookup
1421 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1422 { return (const PosLookupSubTable&) Lookup::get_subtable (i); }
1424 /* Like get_type(), but looks through extension lookups.
1425 * Never returns Extension */
1426 inline unsigned int get_effective_type (void) const
1428 unsigned int type = get_type ();
1430 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
1432 unsigned int count = get_subtable_count ();
1433 type = get_subtable(0).u.extension->get_type ();
1434 /* The spec says all subtables should have the same type.
1435 * This is specially important if one has a reverse type! */
1436 for (unsigned int i = 1; i < count; i++)
1437 if (get_subtable(i).u.extension->get_type () != type)
1444 inline bool apply_once (hb_ot_layout_context_t *context,
1445 hb_buffer_t *buffer,
1446 unsigned int context_length,
1447 unsigned int nesting_level_left) const
1449 unsigned int lookup_type = get_type ();
1450 unsigned int lookup_flag = get_flag ();
1451 unsigned int property;
1453 if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
1456 for (unsigned int i = 0; i < get_subtable_count (); i++)
1457 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
1463 inline bool apply_string (hb_ot_layout_context_t *context,
1464 hb_buffer_t *buffer,
1465 hb_mask_t mask) const
1469 if (HB_UNLIKELY (!buffer->in_length))
1472 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1475 while (buffer->in_pos < buffer->in_length)
1478 if (~IN_MASK (buffer->in_pos) & mask)
1480 done = apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1486 /* Contrary to properties defined in GDEF, user-defined properties
1487 will always stop a possible cursive positioning. */
1488 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1498 inline bool sanitize (SANITIZE_ARG_DEF) {
1500 if (!Lookup::sanitize (SANITIZE_ARG)) return false;
1501 OffsetArrayOf<PosLookupSubTable> &list = (OffsetArrayOf<PosLookupSubTable> &) subTable;
1502 return SANITIZE_THIS (list);
1505 ASSERT_SIZE (PosLookup, 6);
1507 typedef OffsetListOf<PosLookup> PosLookupList;
1508 ASSERT_SIZE (PosLookupList, 2);
1514 struct GPOS : GSUBGPOS
1516 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1518 static inline const GPOS& get_for_data (const char *data)
1519 { return (const GPOS&) GSUBGPOS::get_for_data (data); }
1521 inline const PosLookup& get_lookup (unsigned int i) const
1522 { return (const PosLookup&) GSUBGPOS::get_lookup (i); }
1524 inline bool position_lookup (hb_ot_layout_context_t *context,
1525 hb_buffer_t *buffer,
1526 unsigned int lookup_index,
1527 hb_mask_t mask) const
1528 { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
1530 inline bool sanitize (SANITIZE_ARG_DEF) {
1532 if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
1533 OffsetTo<PosLookupList> &list = CAST(OffsetTo<PosLookupList>, lookupList, 0);
1534 return SANITIZE_THIS (list);
1537 ASSERT_SIZE (GPOS, 10);
1540 /* Out-of-class implementation for methods recursing */
1542 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1544 unsigned int lookup_type = get_type ();
1546 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
1549 return get_subtable ().apply (APPLY_ARG, lookup_type);
1552 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1555 return Extension::sanitize (SANITIZE_ARG) &&
1556 (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
1557 get_type () == PosLookupSubTable::Extension ||
1558 DECONST_CAST (PosLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
1561 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1563 const GPOS &gpos = *(context->face->ot_layout.gpos);
1564 const PosLookup &l = gpos.get_lookup (lookup_index);
1566 if (HB_UNLIKELY (nesting_level_left == 0))
1568 nesting_level_left--;
1570 if (HB_UNLIKELY (context_length < 1))
1573 return l.apply_once (context, buffer, context_length, nesting_level_left);
1577 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */