2 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Red Hat Author(s): Behdad Esfahbod
27 #ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
28 #define HB_OT_LAYOUT_GPOS_PRIVATE_HH
30 #include "hb-ot-layout-gsubgpos-private.hh"
34 #define BUFFER context->buffer
37 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
39 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
43 typedef Value ValueRecord[VAR0];
44 ASSERT_SIZE_VAR (ValueRecord, 0, Value);
46 struct ValueFormat : USHORT
50 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
51 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
52 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
53 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
54 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
55 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
56 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
57 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
58 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
59 reserved = 0xF000, /* For future use */
61 devices = 0x00F0 /* Mask for having any Device table */
64 /* All fields are options. Only those available advance the value pointer. */
66 SHORT xPlacement; /* Horizontal adjustment for
67 * placement--in design units */
68 SHORT yPlacement; /* Vertical adjustment for
69 * placement--in design units */
70 SHORT xAdvance; /* Horizontal adjustment for
71 * advance--in design units (only used
72 * for horizontal writing) */
73 SHORT yAdvance; /* Vertical adjustment for advance--in
74 * design units (only used for vertical
76 Offset xPlaDevice; /* Offset to Device table for
77 * horizontal placement--measured from
78 * beginning of PosTable (may be NULL) */
79 Offset yPlaDevice; /* Offset to Device table for vertical
80 * placement--measured from beginning
81 * of PosTable (may be NULL) */
82 Offset xAdvDevice; /* Offset to Device table for
83 * horizontal advance--measured from
84 * beginning of PosTable (may be NULL) */
85 Offset yAdvDevice; /* Offset to Device table for vertical
86 * advance--measured from beginning of
87 * PosTable (may be NULL) */
90 inline unsigned int get_len () const
91 { return _hb_popcount32 ((unsigned int) *this); }
92 inline unsigned int get_size () const
93 { return get_len () * Value::get_size (); }
95 void apply_value (hb_ot_layout_context_t *layout,
98 hb_internal_glyph_position_t *glyph_pos) const
100 unsigned int x_ppem, y_ppem;
101 hb_16dot16_t x_scale, y_scale;
102 unsigned int format = *this;
106 x_scale = layout->font->x_scale;
107 y_scale = layout->font->y_scale;
108 /* design units -> fractional pixel */
109 if (format & xPlacement) glyph_pos->x_offset += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
110 if (format & yPlacement) glyph_pos->y_offset += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
111 if (format & xAdvance) glyph_pos->x_advance += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
112 if (format & yAdvance) glyph_pos->y_advance += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
114 if (!has_device ()) return;
116 x_ppem = layout->font->x_ppem;
117 y_ppem = layout->font->y_ppem;
119 if (!x_ppem && !y_ppem) return;
121 /* pixel -> fractional pixel */
122 if (format & xPlaDevice) {
123 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
125 if (format & yPlaDevice) {
126 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
128 if (format & xAdvDevice) {
129 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
131 if (format & yAdvDevice) {
132 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
137 inline bool sanitize_value_devices (hb_sanitize_context_t *context, void *base, const Value *values) {
138 unsigned int format = *this;
140 if (format & xPlacement) values++;
141 if (format & yPlacement) values++;
142 if (format & xAdvance) values++;
143 if (format & yAdvance) values++;
145 if ((format & xPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
146 if ((format & yPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
147 if ((format & xAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
148 if ((format & yAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
155 inline bool has_device () const {
156 unsigned int format = *this;
157 return (format & devices) != 0;
160 inline bool sanitize_value (hb_sanitize_context_t *context, void *base, const Value *values) {
163 return SANITIZE_MEM (values, get_size ()) &&
164 (!has_device () || sanitize_value_devices (context, base, values));
167 inline bool sanitize_values (hb_sanitize_context_t *context, void *base, const Value *values, unsigned int count) {
169 unsigned int len = get_len ();
171 if (!SANITIZE_ARRAY (values, get_size (), count)) return false;
173 if (!has_device ()) return true;
175 for (unsigned int i = 0; i < count; i++) {
176 if (!sanitize_value_devices (context, base, values))
184 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
185 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *context, void *base, const Value *values, unsigned int count, unsigned int stride) {
188 if (!has_device ()) return true;
190 for (unsigned int i = 0; i < count; i++) {
191 if (!sanitize_value_devices (context, base, values))
199 ASSERT_SIZE (ValueFormat, 2);
204 friend struct Anchor;
207 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
208 hb_position_t *x, hb_position_t *y) const
210 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
211 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
214 inline bool sanitize (hb_sanitize_context_t *context) {
216 return SANITIZE_SELF ();
220 USHORT format; /* Format identifier--format = 1 */
221 SHORT xCoordinate; /* Horizontal value--in design units */
222 SHORT yCoordinate; /* Vertical value--in design units */
224 ASSERT_SIZE (AnchorFormat1, 6);
228 friend struct Anchor;
231 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
232 hb_position_t *x, hb_position_t *y) const
234 unsigned int x_ppem = layout->font->x_ppem;
235 unsigned int y_ppem = layout->font->y_ppem;
236 hb_position_t cx, cy;
239 if (x_ppem || y_ppem)
240 ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
241 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
242 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
245 inline bool sanitize (hb_sanitize_context_t *context) {
247 return SANITIZE_SELF ();
251 USHORT format; /* Format identifier--format = 2 */
252 SHORT xCoordinate; /* Horizontal value--in design units */
253 SHORT yCoordinate; /* Vertical value--in design units */
254 USHORT anchorPoint; /* Index to glyph contour point */
256 ASSERT_SIZE (AnchorFormat2, 8);
260 friend struct Anchor;
263 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
264 hb_position_t *x, hb_position_t *y) const
266 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
267 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
269 /* pixel -> fractional pixel */
270 if (layout->font->x_ppem)
271 *x += (this+xDeviceTable).get_delta (layout->font->x_ppem) << 16;
272 if (layout->font->y_ppem)
273 *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) << 16;
276 inline bool sanitize (hb_sanitize_context_t *context) {
278 return SANITIZE_SELF ()
279 && SANITIZE_WITH_BASE (this, xDeviceTable)
280 && SANITIZE_WITH_BASE (this, yDeviceTable);
284 USHORT format; /* Format identifier--format = 3 */
285 SHORT xCoordinate; /* Horizontal value--in design units */
286 SHORT yCoordinate; /* Vertical value--in design units */
288 xDeviceTable; /* Offset to Device table for X
289 * coordinate-- from beginning of
290 * Anchor table (may be NULL) */
292 yDeviceTable; /* Offset to Device table for Y
293 * coordinate-- from beginning of
294 * Anchor table (may be NULL) */
296 ASSERT_SIZE (AnchorFormat3, 10);
300 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
301 hb_position_t *x, hb_position_t *y) const
305 case 1: u.format1->get_anchor (layout, glyph_id, x, y); return;
306 case 2: u.format2->get_anchor (layout, glyph_id, x, y); return;
307 case 3: u.format3->get_anchor (layout, glyph_id, x, y); return;
312 inline bool sanitize (hb_sanitize_context_t *context) {
314 if (!SANITIZE (u.format)) return false;
316 case 1: return u.format1->sanitize (context);
317 case 2: return u.format2->sanitize (context);
318 case 3: return u.format3->sanitize (context);
325 USHORT format; /* Format identifier */
326 AnchorFormat1 format1[VAR];
327 AnchorFormat2 format2[VAR];
328 AnchorFormat3 format3[VAR];
335 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
336 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
337 return this+matrix[row * cols + col];
340 inline bool sanitize (hb_sanitize_context_t *context, unsigned int cols) {
342 if (!SANITIZE_SELF ()) return false;
343 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
344 unsigned int count = rows * cols;
345 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
346 for (unsigned int i = 0; i < count; i++)
347 if (!SANITIZE_WITH_BASE (this, matrix[i])) return false;
351 USHORT rows; /* Number of rows */
354 matrix[VAR]; /* Matrix of offsets to Anchor tables--
355 * from beginning of AnchorMatrix table */
357 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
362 friend struct MarkArray;
364 static inline unsigned int get_size () { return sizeof (MarkRecord); }
366 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
368 return SANITIZE_SELF ()
369 && SANITIZE_WITH_BASE (base, markAnchor);
373 USHORT klass; /* Class defined for this mark */
375 markAnchor; /* Offset to Anchor table--from
376 * beginning of MarkArray table */
378 ASSERT_SIZE (MarkRecord, 4);
382 inline bool apply (hb_apply_context_t *context,
383 unsigned int mark_index, unsigned int glyph_index,
384 const AnchorMatrix &anchors, unsigned int class_count,
385 unsigned int glyph_pos) const
388 const MarkRecord &record = markRecord[mark_index];
389 unsigned int mark_class = record.klass;
391 const Anchor& mark_anchor = this + record.markAnchor;
392 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
394 hb_position_t mark_x, mark_y, base_x, base_y;
396 mark_anchor.get_anchor (context->layout, IN_CURGLYPH (), &mark_x, &mark_y);
397 glyph_anchor.get_anchor (context->layout, IN_GLYPH (glyph_pos), &base_x, &base_y);
399 hb_internal_glyph_position_t *o = POSITION (context->buffer->in_pos);
402 o->x_offset = base_x - mark_x;
403 o->y_offset = base_y - mark_y;
404 o->back = context->buffer->in_pos - glyph_pos;
406 context->buffer->in_pos++;
410 inline bool sanitize (hb_sanitize_context_t *context) {
412 return SANITIZE_WITH_BASE (this, markRecord);
417 markRecord; /* Array of MarkRecords--in Coverage order */
419 ASSERT_SIZE (MarkArray, 2);
424 struct SinglePosFormat1
426 friend struct SinglePos;
429 inline bool apply (hb_apply_context_t *context) const
432 unsigned int index = (this+coverage) (IN_CURGLYPH ());
433 if (likely (index == NOT_COVERED))
436 valueFormat.apply_value (context->layout, CharP(this), values, CURPOSITION ());
438 context->buffer->in_pos++;
442 inline bool sanitize (hb_sanitize_context_t *context) {
444 return SANITIZE_SELF ()
445 && SANITIZE_WITH_BASE (this, coverage)
446 && valueFormat.sanitize_value (context, CharP(this), values);
450 USHORT format; /* Format identifier--format = 1 */
452 coverage; /* Offset to Coverage table--from
453 * beginning of subtable */
454 ValueFormat valueFormat; /* Defines the types of data in the
456 ValueRecord values; /* Defines positioning
457 * value(s)--applied to all glyphs in
458 * the Coverage table */
460 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
462 struct SinglePosFormat2
464 friend struct SinglePos;
467 inline bool apply (hb_apply_context_t *context) const
470 unsigned int index = (this+coverage) (IN_CURGLYPH ());
471 if (likely (index == NOT_COVERED))
474 if (likely (index >= valueCount))
477 valueFormat.apply_value (context->layout, CharP(this),
478 &values[index * valueFormat.get_len ()],
481 context->buffer->in_pos++;
485 inline bool sanitize (hb_sanitize_context_t *context) {
487 return SANITIZE_SELF ()
488 && SANITIZE_WITH_BASE (this, coverage)
489 && valueFormat.sanitize_values (context, CharP(this), values, valueCount);
493 USHORT format; /* Format identifier--format = 2 */
495 coverage; /* Offset to Coverage table--from
496 * beginning of subtable */
497 ValueFormat valueFormat; /* Defines the types of data in the
499 USHORT valueCount; /* Number of ValueRecords */
500 ValueRecord values; /* Array of ValueRecords--positioning
501 * values applied to glyphs */
503 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
507 friend struct PosLookupSubTable;
510 inline bool apply (hb_apply_context_t *context) const
514 case 1: return u.format1->apply (context);
515 case 2: return u.format2->apply (context);
516 default:return false;
520 inline bool sanitize (hb_sanitize_context_t *context) {
522 if (!SANITIZE (u.format)) return false;
524 case 1: return u.format1->sanitize (context);
525 case 2: return u.format2->sanitize (context);
532 USHORT format; /* Format identifier */
533 SinglePosFormat1 format1[VAR];
534 SinglePosFormat2 format2[VAR];
539 struct PairValueRecord
541 friend struct PairPosFormat1;
544 GlyphID secondGlyph; /* GlyphID of second glyph in the
545 * pair--first glyph is listed in the
547 ValueRecord values; /* Positioning data for the first glyph
548 * followed by for second glyph */
550 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
554 friend struct PairPosFormat1;
556 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
557 inline bool sanitize (hb_sanitize_context_t *context, unsigned int format_len) {
559 if (!SANITIZE_SELF ()) return false;
560 unsigned int count = (1 + format_len) * len;
561 return SANITIZE_ARRAY (array, USHORT::get_size (), count);
565 USHORT len; /* Number of PairValueRecords */
567 array[VAR]; /* Array of PairValueRecords--ordered
568 * by GlyphID of the second glyph */
570 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
572 struct PairPosFormat1
574 friend struct PairPos;
577 inline bool apply (hb_apply_context_t *context) const
580 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
581 if (unlikely (context->buffer->in_pos + 2 > end))
584 unsigned int index = (this+coverage) (IN_CURGLYPH ());
585 if (likely (index == NOT_COVERED))
588 unsigned int j = context->buffer->in_pos + 1;
589 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
591 if (unlikely (j == end))
596 unsigned int len1 = valueFormat1.get_len ();
597 unsigned int len2 = valueFormat2.get_len ();
598 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
600 const PairSet &pair_set = this+pairSet[index];
601 unsigned int count = pair_set.len;
602 const PairValueRecord *record = pair_set.array;
603 for (unsigned int i = 0; i < count; i++)
605 if (IN_GLYPH (j) == record->secondGlyph)
607 valueFormat1.apply_value (context->layout, CharP(this), &record->values[0], CURPOSITION ());
608 valueFormat2.apply_value (context->layout, CharP(this), &record->values[len1], POSITION (j));
611 context->buffer->in_pos = j;
614 record = &StructAtOffset<PairValueRecord> (*record, record_size);
620 inline bool sanitize (hb_sanitize_context_t *context) {
623 unsigned int len1 = valueFormat1.get_len ();
624 unsigned int len2 = valueFormat2.get_len ();
626 if (!(SANITIZE_SELF ()
627 && SANITIZE_WITH_BASE (this, coverage)
628 && likely (pairSet.sanitize (context, CharP(this), len1 + len2)))) return false;
630 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
632 unsigned int stride = 1 + len1 + len2;
633 unsigned int count1 = pairSet.len;
634 for (unsigned int i = 0; i < count1; i++)
636 const PairSet &pair_set = this+pairSet[i];
638 unsigned int count2 = pair_set.len;
639 const PairValueRecord *record = pair_set.array;
640 if (!(valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &record->values[0], count2, stride) &&
641 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &record->values[len1], count2, stride)))
649 USHORT format; /* Format identifier--format = 1 */
651 coverage; /* Offset to Coverage table--from
652 * beginning of subtable */
653 ValueFormat valueFormat1; /* Defines the types of data in
654 * ValueRecord1--for the first glyph
655 * in the pair--may be zero (0) */
656 ValueFormat valueFormat2; /* Defines the types of data in
657 * ValueRecord2--for the second glyph
658 * in the pair--may be zero (0) */
659 OffsetArrayOf<PairSet>
660 pairSet; /* Array of PairSet tables
661 * ordered by Coverage Index */
663 ASSERT_SIZE (PairPosFormat1, 10);
665 struct PairPosFormat2
667 friend struct PairPos;
670 inline bool apply (hb_apply_context_t *context) const
673 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
674 if (unlikely (context->buffer->in_pos + 2 > end))
677 unsigned int index = (this+coverage) (IN_CURGLYPH ());
678 if (likely (index == NOT_COVERED))
681 unsigned int j = context->buffer->in_pos + 1;
682 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
684 if (unlikely (j == end))
689 unsigned int len1 = valueFormat1.get_len ();
690 unsigned int len2 = valueFormat2.get_len ();
691 unsigned int record_len = len1 + len2;
693 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
694 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
695 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
698 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
699 valueFormat1.apply_value (context->layout, CharP(this), v, CURPOSITION ());
700 valueFormat2.apply_value (context->layout, CharP(this), v + len1, POSITION (j));
704 context->buffer->in_pos = j;
709 inline bool sanitize (hb_sanitize_context_t *context) {
711 if (!(SANITIZE_SELF ()
712 && SANITIZE_WITH_BASE (this, coverage)
713 && SANITIZE_WITH_BASE (this, classDef1)
714 && SANITIZE_WITH_BASE (this, classDef2))) return false;
716 unsigned int len1 = valueFormat1.get_len ();
717 unsigned int len2 = valueFormat2.get_len ();
718 unsigned int stride = len1 + len2;
719 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
720 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
721 return SANITIZE_ARRAY (values, record_size, count) &&
722 valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &values[0], count, stride) &&
723 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &values[len1], count, stride);
727 USHORT format; /* Format identifier--format = 2 */
729 coverage; /* Offset to Coverage table--from
730 * beginning of subtable */
731 ValueFormat valueFormat1; /* ValueRecord definition--for the
732 * first glyph of the pair--may be zero
734 ValueFormat valueFormat2; /* ValueRecord definition--for the
735 * second glyph of the pair--may be
738 classDef1; /* Offset to ClassDef table--from
739 * beginning of PairPos subtable--for
740 * the first glyph of the pair */
742 classDef2; /* Offset to ClassDef table--from
743 * beginning of PairPos subtable--for
744 * the second glyph of the pair */
745 USHORT class1Count; /* Number of classes in ClassDef1
746 * table--includes Class0 */
747 USHORT class2Count; /* Number of classes in ClassDef2
748 * table--includes Class0 */
749 ValueRecord values; /* Matrix of value pairs:
750 * class1-major, class2-minor,
751 * Each entry has value1 and value2 */
753 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
757 friend struct PosLookupSubTable;
760 inline bool apply (hb_apply_context_t *context) const
764 case 1: return u.format1->apply (context);
765 case 2: return u.format2->apply (context);
766 default:return false;
770 inline bool sanitize (hb_sanitize_context_t *context) {
772 if (!SANITIZE (u.format)) return false;
774 case 1: return u.format1->sanitize (context);
775 case 2: return u.format2->sanitize (context);
782 USHORT format; /* Format identifier */
783 PairPosFormat1 format1[VAR];
784 PairPosFormat2 format2[VAR];
789 struct EntryExitRecord
791 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
793 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
795 return SANITIZE_WITH_BASE (base, entryAnchor)
796 && SANITIZE_WITH_BASE (base, exitAnchor);
800 entryAnchor; /* Offset to EntryAnchor table--from
801 * beginning of CursivePos
802 * subtable--may be NULL */
804 exitAnchor; /* Offset to ExitAnchor table--from
805 * beginning of CursivePos
806 * subtable--may be NULL */
808 ASSERT_SIZE (EntryExitRecord, 4);
810 struct CursivePosFormat1
812 friend struct CursivePos;
815 inline bool apply (hb_apply_context_t *context) const
818 /* Now comes the messiest part of the whole OpenType
819 specification. At first glance, cursive connections seem easy
820 to understand, but there are pitfalls! The reason is that
821 the specs don't mention how to compute the advance values
822 resp. glyph offsets. I was told it would be an omission, to
823 be fixed in the next OpenType version... Again many thanks to
824 Andrei Burago <andreib@microsoft.com> for clarifications.
826 Consider the following example:
839 glyph1: advance width = 12
842 glyph2: advance width = 11
845 LSB is 1 for both glyphs (so the boxes drawn above are glyph
846 bboxes). Writing direction is R2L; `0' denotes the glyph's
849 Now the surprising part: The advance width of the *left* glyph
850 (resp. of the *bottom* glyph) will be modified, no matter
851 whether the writing direction is L2R or R2L (resp. T2B or
852 B2T)! This assymetry is caused by the fact that the glyph's
853 coordinate origin is always the lower left corner for all
856 Continuing the above example, we can compute the new
857 (horizontal) advance width of glyph2 as
861 and the new vertical offset of glyph2 as
866 Vertical writing direction is far more complicated:
868 a) Assuming that we recompute the advance height of the lower glyph:
875 yadv2 | 0+--+------+ -- BSB1 --
878 BSB2 -- 0+--------+ --
881 glyph1: advance height = 6
884 glyph2: advance height = 7
887 TSB is 1 for both glyphs; writing direction is T2B.
890 BSB1 = yadv1 - (TSB1 + ymax1)
891 BSB2 = yadv2 - (TSB2 + ymax2)
894 vertical advance width of glyph2
895 = y_offset + BSB2 - BSB1
896 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
897 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
898 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
901 b) Assuming that we recompute the advance height of the upper glyph:
906 TSB2 -- +-----+--+ 1 | yadv1 ymax1
908 yadv2 | 0+--+------+ -- --
909 ymax2 | 2 | -- y_offset
914 glyph1: advance height = 6
917 glyph2: advance height = 7
920 TSB is 1 for both glyphs; writing direction is T2B.
924 vertical advance width of glyph2
925 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
926 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
929 Comparing a) with b) shows that b) is easier to compute. I'll wait
930 for a reply from Andrei to see what should really be implemented...
932 Since horizontal advance widths or vertical advance heights
933 can be used alone but not together, no ambiguity occurs. */
935 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
936 hb_codepoint_t last_pos = gpi->last;
937 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
939 /* We don't handle mark glyphs here. */
940 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
943 unsigned int index = (this+coverage) (IN_CURGLYPH ());
944 if (likely (index == NOT_COVERED))
947 const EntryExitRecord &record = entryExitRecord[index];
949 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
952 hb_position_t entry_x, entry_y;
953 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
957 if (context->buffer->direction == HB_DIRECTION_RTL)
959 /* advance is absolute, not relative */
960 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
964 /* advance is absolute, not relative */
965 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
968 if (context->lookup_flag & LookupFlag::RightToLeft)
970 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
971 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
975 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
976 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
980 if (record.exitAnchor)
982 gpi->last = context->buffer->in_pos;
983 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
986 context->buffer->in_pos++;
990 inline bool sanitize (hb_sanitize_context_t *context) {
992 return SANITIZE_WITH_BASE (this, coverage)
993 && SANITIZE_WITH_BASE (this, entryExitRecord);
997 USHORT format; /* Format identifier--format = 1 */
999 coverage; /* Offset to Coverage table--from
1000 * beginning of subtable */
1001 ArrayOf<EntryExitRecord>
1002 entryExitRecord; /* Array of EntryExit records--in
1003 * Coverage Index order */
1005 ASSERT_SIZE (CursivePosFormat1, 6);
1009 friend struct PosLookupSubTable;
1012 inline bool apply (hb_apply_context_t *context) const
1016 case 1: return u.format1->apply (context);
1017 default:return false;
1021 inline bool sanitize (hb_sanitize_context_t *context) {
1023 if (!SANITIZE (u.format)) return false;
1025 case 1: return u.format1->sanitize (context);
1026 default:return true;
1032 USHORT format; /* Format identifier */
1033 CursivePosFormat1 format1[VAR];
1038 typedef AnchorMatrix BaseArray; /* base-major--
1039 * in order of BaseCoverage Index--,
1041 * ordered by class--zero-based. */
1043 struct MarkBasePosFormat1
1045 friend struct MarkBasePos;
1048 inline bool apply (hb_apply_context_t *context) const
1051 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1052 if (likely (mark_index == NOT_COVERED))
1055 /* now we search backwards for a non-mark glyph */
1056 unsigned int property;
1057 unsigned int j = context->buffer->in_pos;
1063 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1066 /* The following assertion is too strong. */
1067 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1071 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1072 if (base_index == NOT_COVERED)
1075 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1078 inline bool sanitize (hb_sanitize_context_t *context) {
1080 return SANITIZE_SELF ()
1081 && SANITIZE_WITH_BASE (this, markCoverage)
1082 && SANITIZE_WITH_BASE (this, baseCoverage)
1083 && SANITIZE_WITH_BASE (this, markArray)
1084 && likely (baseArray.sanitize (context, CharP(this), (unsigned int) classCount));
1088 USHORT format; /* Format identifier--format = 1 */
1090 markCoverage; /* Offset to MarkCoverage table--from
1091 * beginning of MarkBasePos subtable */
1093 baseCoverage; /* Offset to BaseCoverage table--from
1094 * beginning of MarkBasePos subtable */
1095 USHORT classCount; /* Number of classes defined for marks */
1097 markArray; /* Offset to MarkArray table--from
1098 * beginning of MarkBasePos subtable */
1100 baseArray; /* Offset to BaseArray table--from
1101 * beginning of MarkBasePos subtable */
1103 ASSERT_SIZE (MarkBasePosFormat1, 12);
1107 friend struct PosLookupSubTable;
1110 inline bool apply (hb_apply_context_t *context) const
1114 case 1: return u.format1->apply (context);
1115 default:return false;
1119 inline bool sanitize (hb_sanitize_context_t *context) {
1121 if (!SANITIZE (u.format)) return false;
1123 case 1: return u.format1->sanitize (context);
1124 default:return true;
1130 USHORT format; /* Format identifier */
1131 MarkBasePosFormat1 format1[VAR];
1136 typedef AnchorMatrix LigatureAttach; /* component-major--
1137 * in order of writing direction--,
1139 * ordered by class--zero-based. */
1141 typedef OffsetListOf<LigatureAttach> LigatureArray;
1142 /* Array of LigatureAttach
1144 * LigatureCoverage Index */
1146 struct MarkLigPosFormat1
1148 friend struct MarkLigPos;
1151 inline bool apply (hb_apply_context_t *context) const
1154 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1155 if (likely (mark_index == NOT_COVERED))
1158 /* now we search backwards for a non-mark glyph */
1159 unsigned int property;
1160 unsigned int j = context->buffer->in_pos;
1166 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1169 /* The following assertion is too strong. */
1170 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1174 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1175 if (lig_index == NOT_COVERED)
1178 const LigatureArray& lig_array = this+ligatureArray;
1179 const LigatureAttach& lig_attach = lig_array[lig_index];
1181 /* Find component to attach to */
1182 unsigned int comp_count = lig_attach.rows;
1183 if (unlikely (!comp_count))
1185 unsigned int comp_index;
1186 /* We must now check whether the ligature ID of the current mark glyph
1187 * is identical to the ligature ID of the found ligature. If yes, we
1188 * can directly use the component index. If not, we attach the mark
1189 * glyph to the last component of the ligature. */
1190 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1192 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1193 if (comp_index >= comp_count)
1194 comp_index = comp_count - 1;
1197 comp_index = comp_count - 1;
1199 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1202 inline bool sanitize (hb_sanitize_context_t *context) {
1204 return SANITIZE_SELF ()
1205 && SANITIZE_WITH_BASE (this, markCoverage)
1206 && SANITIZE_WITH_BASE (this, ligatureCoverage)
1207 && SANITIZE_WITH_BASE (this, markArray)
1208 && likely (ligatureArray.sanitize (context, CharP(this), (unsigned int) classCount));
1212 USHORT format; /* Format identifier--format = 1 */
1214 markCoverage; /* Offset to Mark Coverage table--from
1215 * beginning of MarkLigPos subtable */
1217 ligatureCoverage; /* Offset to Ligature Coverage
1218 * table--from beginning of MarkLigPos
1220 USHORT classCount; /* Number of defined mark classes */
1222 markArray; /* Offset to MarkArray table--from
1223 * beginning of MarkLigPos subtable */
1224 OffsetTo<LigatureArray>
1225 ligatureArray; /* Offset to LigatureArray table--from
1226 * beginning of MarkLigPos subtable */
1228 ASSERT_SIZE (MarkLigPosFormat1, 12);
1232 friend struct PosLookupSubTable;
1235 inline bool apply (hb_apply_context_t *context) const
1239 case 1: return u.format1->apply (context);
1240 default:return false;
1244 inline bool sanitize (hb_sanitize_context_t *context) {
1246 if (!SANITIZE (u.format)) return false;
1248 case 1: return u.format1->sanitize (context);
1249 default:return true;
1255 USHORT format; /* Format identifier */
1256 MarkLigPosFormat1 format1[VAR];
1261 typedef AnchorMatrix Mark2Array; /* mark2-major--
1262 * in order of Mark2Coverage Index--,
1264 * ordered by class--zero-based. */
1266 struct MarkMarkPosFormat1
1268 friend struct MarkMarkPos;
1271 inline bool apply (hb_apply_context_t *context) const
1274 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1275 if (likely (mark1_index == NOT_COVERED))
1278 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1279 unsigned int property;
1280 unsigned int j = context->buffer->in_pos;
1286 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1288 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1291 /* Two marks match only if they belong to the same base, or same component
1292 * of the same ligature. That is, the component numbers must match, and
1293 * if those are non-zero, the ligid number should also match. */
1294 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1295 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1298 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1299 if (mark2_index == NOT_COVERED)
1302 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1305 inline bool sanitize (hb_sanitize_context_t *context) {
1307 return SANITIZE_SELF ()
1308 && SANITIZE_WITH_BASE (this, mark1Coverage)
1309 && SANITIZE_WITH_BASE (this, mark2Coverage)
1310 && SANITIZE_WITH_BASE (this, mark1Array)
1311 && likely (mark2Array.sanitize (context, CharP(this), (unsigned int) classCount));
1315 USHORT format; /* Format identifier--format = 1 */
1317 mark1Coverage; /* Offset to Combining Mark1 Coverage
1318 * table--from beginning of MarkMarkPos
1321 mark2Coverage; /* Offset to Combining Mark2 Coverage
1322 * table--from beginning of MarkMarkPos
1324 USHORT classCount; /* Number of defined mark classes */
1326 mark1Array; /* Offset to Mark1Array table--from
1327 * beginning of MarkMarkPos subtable */
1328 OffsetTo<Mark2Array>
1329 mark2Array; /* Offset to Mark2Array table--from
1330 * beginning of MarkMarkPos subtable */
1332 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1336 friend struct PosLookupSubTable;
1339 inline bool apply (hb_apply_context_t *context) const
1343 case 1: return u.format1->apply (context);
1344 default:return false;
1348 inline bool sanitize (hb_sanitize_context_t *context) {
1350 if (!SANITIZE (u.format)) return false;
1352 case 1: return u.format1->sanitize (context);
1353 default:return true;
1359 USHORT format; /* Format identifier */
1360 MarkMarkPosFormat1 format1[VAR];
1365 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1367 struct ContextPos : Context
1369 friend struct PosLookupSubTable;
1372 inline bool apply (hb_apply_context_t *context) const
1375 return Context::apply (context, position_lookup);
1379 struct ChainContextPos : ChainContext
1381 friend struct PosLookupSubTable;
1384 inline bool apply (hb_apply_context_t *context) const
1387 return ChainContext::apply (context, position_lookup);
1392 struct ExtensionPos : Extension
1394 friend struct PosLookupSubTable;
1397 inline const struct PosLookupSubTable& get_subtable (void) const
1399 unsigned int offset = get_offset ();
1400 if (unlikely (!offset)) return Null(PosLookupSubTable);
1401 return StructAtOffset<PosLookupSubTable> (*this, offset);
1404 inline bool apply (hb_apply_context_t *context) const;
1406 inline bool sanitize (hb_sanitize_context_t *context);
1416 struct PosLookupSubTable
1418 friend struct PosLookup;
1432 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1435 switch (lookup_type) {
1436 case Single: return u.single->apply (context);
1437 case Pair: return u.pair->apply (context);
1438 case Cursive: return u.cursive->apply (context);
1439 case MarkBase: return u.markBase->apply (context);
1440 case MarkLig: return u.markLig->apply (context);
1441 case MarkMark: return u.markMark->apply (context);
1442 case Context: return u.context->apply (context);
1443 case ChainContext: return u.chainContext->apply (context);
1444 case Extension: return u.extension->apply (context);
1445 default:return false;
1449 inline bool sanitize (hb_sanitize_context_t *context) {
1451 if (!SANITIZE (u.format)) return false;
1453 case Single: return u.single->sanitize (context);
1454 case Pair: return u.pair->sanitize (context);
1455 case Cursive: return u.cursive->sanitize (context);
1456 case MarkBase: return u.markBase->sanitize (context);
1457 case MarkLig: return u.markLig->sanitize (context);
1458 case MarkMark: return u.markMark->sanitize (context);
1459 case Context: return u.context->sanitize (context);
1460 case ChainContext: return u.chainContext->sanitize (context);
1461 case Extension: return u.extension->sanitize (context);
1462 default:return true;
1469 SinglePos single[VAR];
1471 CursivePos cursive[VAR];
1472 MarkBasePos markBase[VAR];
1473 MarkLigPos markLig[VAR];
1474 MarkMarkPos markMark[VAR];
1475 ContextPos context[VAR];
1476 ChainContextPos chainContext[VAR];
1477 ExtensionPos extension[VAR];
1482 struct PosLookup : Lookup
1484 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1485 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1487 inline bool apply_once (hb_ot_layout_context_t *layout,
1488 hb_buffer_t *buffer,
1489 unsigned int context_length,
1490 unsigned int nesting_level_left) const
1492 unsigned int lookup_type = get_type ();
1493 hb_apply_context_t context[1] = {{}};
1495 context->layout = layout;
1496 context->buffer = buffer;
1497 context->context_length = context_length;
1498 context->nesting_level_left = nesting_level_left;
1499 context->lookup_flag = get_flag ();
1501 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1504 for (unsigned int i = 0; i < get_subtable_count (); i++)
1505 if (get_subtable (i).apply (context, lookup_type))
1511 inline bool apply_string (hb_ot_layout_context_t *layout,
1512 hb_buffer_t *buffer,
1513 hb_mask_t mask) const
1516 #define BUFFER buffer
1519 if (unlikely (!buffer->in_length))
1522 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1525 while (buffer->in_pos < buffer->in_length)
1528 if (~IN_MASK (buffer->in_pos) & mask)
1530 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1536 /* Contrary to properties defined in GDEF, user-defined properties
1537 will always stop a possible cursive positioning. */
1538 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1548 inline bool sanitize (hb_sanitize_context_t *context) {
1550 if (unlikely (!Lookup::sanitize (context))) return false;
1551 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1552 return SANITIZE_WITH_BASE (this, list);
1556 typedef OffsetListOf<PosLookup> PosLookupList;
1557 ASSERT_SIZE (PosLookupList, 2);
1563 struct GPOS : GSUBGPOS
1565 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1567 inline const PosLookup& get_lookup (unsigned int i) const
1568 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1570 inline bool position_lookup (hb_ot_layout_context_t *layout,
1571 hb_buffer_t *buffer,
1572 unsigned int lookup_index,
1573 hb_mask_t mask) const
1574 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1576 inline bool sanitize (hb_sanitize_context_t *context) {
1578 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1579 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1580 return SANITIZE_WITH_BASE (this, list);
1583 ASSERT_SIZE (GPOS, 10);
1586 /* Out-of-class implementation for methods recursing */
1588 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1591 return get_subtable ().apply (context, get_type ());
1594 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1597 if (unlikely (!Extension::sanitize (context))) return false;
1598 unsigned int offset = get_offset ();
1599 if (unlikely (!offset)) return true;
1600 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1603 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1605 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1606 const PosLookup &l = gpos.get_lookup (lookup_index);
1608 if (unlikely (context->nesting_level_left == 0))
1611 if (unlikely (context->context_length < 1))
1614 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1618 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */