2 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Red Hat Author(s): Behdad Esfahbod
27 #ifndef HB_OT_LAYOUT_GPOS_PRIVATE_HH
28 #define HB_OT_LAYOUT_GPOS_PRIVATE_HH
30 #include "hb-ot-layout-gsubgpos-private.hh"
32 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
34 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
38 typedef Value ValueRecord[VAR0];
39 ASSERT_SIZE_VAR (ValueRecord, 0, Value);
41 struct ValueFormat : USHORT
45 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
46 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
47 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
48 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
49 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
50 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
51 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
52 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
53 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
54 reserved = 0xF000, /* For future use */
56 devices = 0x00F0 /* Mask for having any Device table */
59 /* All fields are options. Only those available advance the value pointer. */
61 SHORT xPlacement; /* Horizontal adjustment for
62 * placement--in design units */
63 SHORT yPlacement; /* Vertical adjustment for
64 * placement--in design units */
65 SHORT xAdvance; /* Horizontal adjustment for
66 * advance--in design units (only used
67 * for horizontal writing) */
68 SHORT yAdvance; /* Vertical adjustment for advance--in
69 * design units (only used for vertical
71 Offset xPlaDevice; /* Offset to Device table for
72 * horizontal placement--measured from
73 * beginning of PosTable (may be NULL) */
74 Offset yPlaDevice; /* Offset to Device table for vertical
75 * placement--measured from beginning
76 * of PosTable (may be NULL) */
77 Offset xAdvDevice; /* Offset to Device table for
78 * horizontal advance--measured from
79 * beginning of PosTable (may be NULL) */
80 Offset yAdvDevice; /* Offset to Device table for vertical
81 * advance--measured from beginning of
82 * PosTable (may be NULL) */
85 inline unsigned int get_len () const
86 { return _hb_popcount32 ((unsigned int) *this); }
87 inline unsigned int get_size () const
88 { return get_len () * Value::get_size (); }
90 void apply_value (hb_ot_layout_context_t *layout_context,
93 hb_internal_glyph_position_t *glyph_pos) const
95 unsigned int x_ppem, y_ppem;
96 hb_16dot16_t x_scale, y_scale;
97 unsigned int format = *this;
101 x_scale = layout_context->font->x_scale;
102 y_scale = layout_context->font->y_scale;
103 /* design units -> fractional pixel */
104 if (format & xPlacement) glyph_pos->x_offset += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
105 if (format & yPlacement) glyph_pos->y_offset += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
106 if (format & xAdvance) glyph_pos->x_advance += _hb_16dot16_mul_round (x_scale, *(SHORT*)values++);
107 if (format & yAdvance) glyph_pos->y_advance += _hb_16dot16_mul_round (y_scale, *(SHORT*)values++);
109 if (!has_device ()) return;
111 x_ppem = layout_context->font->x_ppem;
112 y_ppem = layout_context->font->y_ppem;
114 if (!x_ppem && !y_ppem) return;
116 /* pixel -> fractional pixel */
117 if (format & xPlaDevice) {
118 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
120 if (format & yPlaDevice) {
121 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
123 if (format & xAdvDevice) {
124 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
126 if (format & yAdvDevice) {
127 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
132 inline bool sanitize_value_devices (SANITIZE_ARG_DEF, void *base, const Value *values) {
133 unsigned int format = *this;
135 if (format & xPlacement) values++;
136 if (format & yPlacement) values++;
137 if (format & xAdvance) values++;
138 if (format & yAdvance) values++;
140 if ((format & xPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
141 if ((format & yPlaDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
142 if ((format & xAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
143 if ((format & yAdvDevice) && !SANITIZE_WITH_BASE (base, *(OffsetTo<Device>*)values++)) return false;
150 inline bool has_device () const {
151 unsigned int format = *this;
152 return (format & devices) != 0;
155 inline bool sanitize_value (SANITIZE_ARG_DEF, void *base, const Value *values) {
158 return SANITIZE_MEM (values, get_size ()) &&
159 (!has_device () || sanitize_value_devices (SANITIZE_ARG, base, values));
162 inline bool sanitize_values (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count) {
164 unsigned int len = get_len ();
166 if (!SANITIZE_ARRAY (values, get_size (), count)) return false;
168 if (!has_device ()) return true;
170 for (unsigned int i = 0; i < count; i++) {
171 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
179 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
180 inline bool sanitize_values_stride_unsafe (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count, unsigned int stride) {
183 if (!has_device ()) return true;
185 for (unsigned int i = 0; i < count; i++) {
186 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
194 ASSERT_SIZE (ValueFormat, 2);
199 friend struct Anchor;
202 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
203 hb_position_t *x, hb_position_t *y) const
205 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
206 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
209 inline bool sanitize (SANITIZE_ARG_DEF) {
211 return SANITIZE_SELF ();
215 USHORT format; /* Format identifier--format = 1 */
216 SHORT xCoordinate; /* Horizontal value--in design units */
217 SHORT yCoordinate; /* Vertical value--in design units */
219 ASSERT_SIZE (AnchorFormat1, 6);
223 friend struct Anchor;
226 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
227 hb_position_t *x, hb_position_t *y) const
229 unsigned int x_ppem = layout_context->font->x_ppem;
230 unsigned int y_ppem = layout_context->font->y_ppem;
231 hb_position_t cx, cy;
234 if (x_ppem || y_ppem)
235 ret = hb_font_get_contour_point (layout_context->font, layout_context->face, anchorPoint, glyph_id, &cx, &cy);
236 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
237 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
240 inline bool sanitize (SANITIZE_ARG_DEF) {
242 return SANITIZE_SELF ();
246 USHORT format; /* Format identifier--format = 2 */
247 SHORT xCoordinate; /* Horizontal value--in design units */
248 SHORT yCoordinate; /* Vertical value--in design units */
249 USHORT anchorPoint; /* Index to glyph contour point */
251 ASSERT_SIZE (AnchorFormat2, 8);
255 friend struct Anchor;
258 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
259 hb_position_t *x, hb_position_t *y) const
261 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
262 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
264 /* pixel -> fractional pixel */
265 if (layout_context->font->x_ppem)
266 *x += (this+xDeviceTable).get_delta (layout_context->font->x_ppem) << 16;
267 if (layout_context->font->y_ppem)
268 *y += (this+yDeviceTable).get_delta (layout_context->font->y_ppem) << 16;
271 inline bool sanitize (SANITIZE_ARG_DEF) {
273 return SANITIZE_SELF ()
274 && SANITIZE_WITH_BASE (this, xDeviceTable)
275 && SANITIZE_WITH_BASE (this, yDeviceTable);
279 USHORT format; /* Format identifier--format = 3 */
280 SHORT xCoordinate; /* Horizontal value--in design units */
281 SHORT yCoordinate; /* Vertical value--in design units */
283 xDeviceTable; /* Offset to Device table for X
284 * coordinate-- from beginning of
285 * Anchor table (may be NULL) */
287 yDeviceTable; /* Offset to Device table for Y
288 * coordinate-- from beginning of
289 * Anchor table (may be NULL) */
291 ASSERT_SIZE (AnchorFormat3, 10);
295 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
296 hb_position_t *x, hb_position_t *y) const
300 case 1: u.format1->get_anchor (layout_context, glyph_id, x, y); return;
301 case 2: u.format2->get_anchor (layout_context, glyph_id, x, y); return;
302 case 3: u.format3->get_anchor (layout_context, glyph_id, x, y); return;
307 inline bool sanitize (SANITIZE_ARG_DEF) {
309 if (!SANITIZE (u.format)) return false;
311 case 1: return u.format1->sanitize (SANITIZE_ARG);
312 case 2: return u.format2->sanitize (SANITIZE_ARG);
313 case 3: return u.format3->sanitize (SANITIZE_ARG);
320 USHORT format; /* Format identifier */
321 AnchorFormat1 format1[VAR];
322 AnchorFormat2 format2[VAR];
323 AnchorFormat3 format3[VAR];
330 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
331 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
332 return this+matrix[row * cols + col];
335 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
337 if (!SANITIZE_SELF ()) return false;
338 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
339 unsigned int count = rows * cols;
340 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
341 for (unsigned int i = 0; i < count; i++)
342 if (!SANITIZE_WITH_BASE (this, matrix[i])) return false;
346 USHORT rows; /* Number of rows */
349 matrix[VAR]; /* Matrix of offsets to Anchor tables--
350 * from beginning of AnchorMatrix table */
352 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
357 friend struct MarkArray;
359 static inline unsigned int get_size () { return sizeof (MarkRecord); }
361 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
363 return SANITIZE_SELF ()
364 && SANITIZE_WITH_BASE (base, markAnchor);
368 USHORT klass; /* Class defined for this mark */
370 markAnchor; /* Offset to Anchor table--from
371 * beginning of MarkArray table */
373 ASSERT_SIZE (MarkRecord, 4);
377 inline bool apply (APPLY_ARG_DEF,
378 unsigned int mark_index, unsigned int glyph_index,
379 const AnchorMatrix &anchors, unsigned int class_count,
380 unsigned int glyph_pos) const
383 const MarkRecord &record = markRecord[mark_index];
384 unsigned int mark_class = record.klass;
386 const Anchor& mark_anchor = this + record.markAnchor;
387 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
389 hb_position_t mark_x, mark_y, base_x, base_y;
391 mark_anchor.get_anchor (layout_context, IN_CURGLYPH (), &mark_x, &mark_y);
392 glyph_anchor.get_anchor (layout_context, IN_GLYPH (glyph_pos), &base_x, &base_y);
394 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
397 o->x_offset = base_x - mark_x;
398 o->y_offset = base_y - mark_y;
399 o->back = buffer->in_pos - glyph_pos;
405 inline bool sanitize (SANITIZE_ARG_DEF) {
407 return SANITIZE_WITH_BASE (this, markRecord);
412 markRecord; /* Array of MarkRecords--in Coverage order */
414 ASSERT_SIZE (MarkArray, 2);
419 struct SinglePosFormat1
421 friend struct SinglePos;
424 inline bool apply (APPLY_ARG_DEF) const
427 unsigned int index = (this+coverage) (IN_CURGLYPH ());
428 if (likely (index == NOT_COVERED))
431 valueFormat.apply_value (layout_context, CharP(this), values, CURPOSITION ());
437 inline bool sanitize (SANITIZE_ARG_DEF) {
439 return SANITIZE_SELF ()
440 && SANITIZE_WITH_BASE (this, coverage)
441 && valueFormat.sanitize_value (SANITIZE_ARG, CharP(this), values);
445 USHORT format; /* Format identifier--format = 1 */
447 coverage; /* Offset to Coverage table--from
448 * beginning of subtable */
449 ValueFormat valueFormat; /* Defines the types of data in the
451 ValueRecord values; /* Defines positioning
452 * value(s)--applied to all glyphs in
453 * the Coverage table */
455 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
457 struct SinglePosFormat2
459 friend struct SinglePos;
462 inline bool apply (APPLY_ARG_DEF) const
465 unsigned int index = (this+coverage) (IN_CURGLYPH ());
466 if (likely (index == NOT_COVERED))
469 if (likely (index >= valueCount))
472 valueFormat.apply_value (layout_context, CharP(this),
473 &values[index * valueFormat.get_len ()],
480 inline bool sanitize (SANITIZE_ARG_DEF) {
482 return SANITIZE_SELF ()
483 && SANITIZE_WITH_BASE (this, coverage)
484 && valueFormat.sanitize_values (SANITIZE_ARG, CharP(this), values, valueCount);
488 USHORT format; /* Format identifier--format = 2 */
490 coverage; /* Offset to Coverage table--from
491 * beginning of subtable */
492 ValueFormat valueFormat; /* Defines the types of data in the
494 USHORT valueCount; /* Number of ValueRecords */
495 ValueRecord values; /* Array of ValueRecords--positioning
496 * values applied to glyphs */
498 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
502 friend struct PosLookupSubTable;
505 inline bool apply (APPLY_ARG_DEF) const
509 case 1: return u.format1->apply (APPLY_ARG);
510 case 2: return u.format2->apply (APPLY_ARG);
511 default:return false;
515 inline bool sanitize (SANITIZE_ARG_DEF) {
517 if (!SANITIZE (u.format)) return false;
519 case 1: return u.format1->sanitize (SANITIZE_ARG);
520 case 2: return u.format2->sanitize (SANITIZE_ARG);
527 USHORT format; /* Format identifier */
528 SinglePosFormat1 format1[VAR];
529 SinglePosFormat2 format2[VAR];
534 struct PairValueRecord
536 friend struct PairPosFormat1;
539 GlyphID secondGlyph; /* GlyphID of second glyph in the
540 * pair--first glyph is listed in the
542 ValueRecord values; /* Positioning data for the first glyph
543 * followed by for second glyph */
545 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
549 friend struct PairPosFormat1;
551 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
552 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
554 if (!SANITIZE_SELF ()) return false;
555 unsigned int count = (1 + format_len) * len;
556 return SANITIZE_ARRAY (array, USHORT::get_size (), count);
560 USHORT len; /* Number of PairValueRecords */
562 array[VAR]; /* Array of PairValueRecords--ordered
563 * by GlyphID of the second glyph */
565 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
567 struct PairPosFormat1
569 friend struct PairPos;
572 inline bool apply (APPLY_ARG_DEF) const
575 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
576 if (unlikely (buffer->in_pos + 2 > end))
579 unsigned int index = (this+coverage) (IN_CURGLYPH ());
580 if (likely (index == NOT_COVERED))
583 unsigned int j = buffer->in_pos + 1;
584 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
586 if (unlikely (j == end))
591 unsigned int len1 = valueFormat1.get_len ();
592 unsigned int len2 = valueFormat2.get_len ();
593 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
595 const PairSet &pair_set = this+pairSet[index];
596 unsigned int count = pair_set.len;
597 const PairValueRecord *record = pair_set.array;
598 for (unsigned int i = 0; i < count; i++)
600 if (IN_GLYPH (j) == record->secondGlyph)
602 valueFormat1.apply_value (layout_context, CharP(this), &record->values[0], CURPOSITION ());
603 valueFormat2.apply_value (layout_context, CharP(this), &record->values[len1], POSITION (j));
609 record = &StructAtOffset<PairValueRecord> (*record, record_size);
615 inline bool sanitize (SANITIZE_ARG_DEF) {
618 unsigned int len1 = valueFormat1.get_len ();
619 unsigned int len2 = valueFormat2.get_len ();
621 if (!(SANITIZE_SELF ()
622 && SANITIZE_WITH_BASE (this, coverage)
623 && likely (pairSet.sanitize (SANITIZE_ARG, CharP(this), len1 + len2)))) return false;
625 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
627 unsigned int stride = 1 + len1 + len2;
628 unsigned int count1 = pairSet.len;
629 for (unsigned int i = 0; i < count1; i++)
631 const PairSet &pair_set = this+pairSet[i];
633 unsigned int count2 = pair_set.len;
634 const PairValueRecord *record = pair_set.array;
635 if (!(valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[0], count2, stride) &&
636 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[len1], count2, stride)))
644 USHORT format; /* Format identifier--format = 1 */
646 coverage; /* Offset to Coverage table--from
647 * beginning of subtable */
648 ValueFormat valueFormat1; /* Defines the types of data in
649 * ValueRecord1--for the first glyph
650 * in the pair--may be zero (0) */
651 ValueFormat valueFormat2; /* Defines the types of data in
652 * ValueRecord2--for the second glyph
653 * in the pair--may be zero (0) */
654 OffsetArrayOf<PairSet>
655 pairSet; /* Array of PairSet tables
656 * ordered by Coverage Index */
658 ASSERT_SIZE (PairPosFormat1, 10);
660 struct PairPosFormat2
662 friend struct PairPos;
665 inline bool apply (APPLY_ARG_DEF) const
668 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
669 if (unlikely (buffer->in_pos + 2 > end))
672 unsigned int index = (this+coverage) (IN_CURGLYPH ());
673 if (likely (index == NOT_COVERED))
676 unsigned int j = buffer->in_pos + 1;
677 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
679 if (unlikely (j == end))
684 unsigned int len1 = valueFormat1.get_len ();
685 unsigned int len2 = valueFormat2.get_len ();
686 unsigned int record_len = len1 + len2;
688 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
689 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
690 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
693 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
694 valueFormat1.apply_value (layout_context, CharP(this), v, CURPOSITION ());
695 valueFormat2.apply_value (layout_context, CharP(this), v + len1, POSITION (j));
704 inline bool sanitize (SANITIZE_ARG_DEF) {
706 if (!(SANITIZE_SELF ()
707 && SANITIZE_WITH_BASE (this, coverage)
708 && SANITIZE_WITH_BASE (this, classDef1)
709 && SANITIZE_WITH_BASE (this, classDef2))) return false;
711 unsigned int len1 = valueFormat1.get_len ();
712 unsigned int len2 = valueFormat2.get_len ();
713 unsigned int stride = len1 + len2;
714 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
715 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
716 return SANITIZE_ARRAY (values, record_size, count) &&
717 valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[0], count, stride) &&
718 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[len1], count, stride);
722 USHORT format; /* Format identifier--format = 2 */
724 coverage; /* Offset to Coverage table--from
725 * beginning of subtable */
726 ValueFormat valueFormat1; /* ValueRecord definition--for the
727 * first glyph of the pair--may be zero
729 ValueFormat valueFormat2; /* ValueRecord definition--for the
730 * second glyph of the pair--may be
733 classDef1; /* Offset to ClassDef table--from
734 * beginning of PairPos subtable--for
735 * the first glyph of the pair */
737 classDef2; /* Offset to ClassDef table--from
738 * beginning of PairPos subtable--for
739 * the second glyph of the pair */
740 USHORT class1Count; /* Number of classes in ClassDef1
741 * table--includes Class0 */
742 USHORT class2Count; /* Number of classes in ClassDef2
743 * table--includes Class0 */
744 ValueRecord values; /* Matrix of value pairs:
745 * class1-major, class2-minor,
746 * Each entry has value1 and value2 */
748 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
752 friend struct PosLookupSubTable;
755 inline bool apply (APPLY_ARG_DEF) const
759 case 1: return u.format1->apply (APPLY_ARG);
760 case 2: return u.format2->apply (APPLY_ARG);
761 default:return false;
765 inline bool sanitize (SANITIZE_ARG_DEF) {
767 if (!SANITIZE (u.format)) return false;
769 case 1: return u.format1->sanitize (SANITIZE_ARG);
770 case 2: return u.format2->sanitize (SANITIZE_ARG);
777 USHORT format; /* Format identifier */
778 PairPosFormat1 format1[VAR];
779 PairPosFormat2 format2[VAR];
784 struct EntryExitRecord
786 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
788 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
790 return SANITIZE_WITH_BASE (base, entryAnchor)
791 && SANITIZE_WITH_BASE (base, exitAnchor);
795 entryAnchor; /* Offset to EntryAnchor table--from
796 * beginning of CursivePos
797 * subtable--may be NULL */
799 exitAnchor; /* Offset to ExitAnchor table--from
800 * beginning of CursivePos
801 * subtable--may be NULL */
803 ASSERT_SIZE (EntryExitRecord, 4);
805 struct CursivePosFormat1
807 friend struct CursivePos;
810 inline bool apply (APPLY_ARG_DEF) const
813 /* Now comes the messiest part of the whole OpenType
814 specification. At first glance, cursive connections seem easy
815 to understand, but there are pitfalls! The reason is that
816 the specs don't mention how to compute the advance values
817 resp. glyph offsets. I was told it would be an omission, to
818 be fixed in the next OpenType version... Again many thanks to
819 Andrei Burago <andreib@microsoft.com> for clarifications.
821 Consider the following example:
834 glyph1: advance width = 12
837 glyph2: advance width = 11
840 LSB is 1 for both glyphs (so the boxes drawn above are glyph
841 bboxes). Writing direction is R2L; `0' denotes the glyph's
844 Now the surprising part: The advance width of the *left* glyph
845 (resp. of the *bottom* glyph) will be modified, no matter
846 whether the writing direction is L2R or R2L (resp. T2B or
847 B2T)! This assymetry is caused by the fact that the glyph's
848 coordinate origin is always the lower left corner for all
851 Continuing the above example, we can compute the new
852 (horizontal) advance width of glyph2 as
856 and the new vertical offset of glyph2 as
861 Vertical writing direction is far more complicated:
863 a) Assuming that we recompute the advance height of the lower glyph:
870 yadv2 | 0+--+------+ -- BSB1 --
873 BSB2 -- 0+--------+ --
876 glyph1: advance height = 6
879 glyph2: advance height = 7
882 TSB is 1 for both glyphs; writing direction is T2B.
885 BSB1 = yadv1 - (TSB1 + ymax1)
886 BSB2 = yadv2 - (TSB2 + ymax2)
889 vertical advance width of glyph2
890 = y_offset + BSB2 - BSB1
891 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
892 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
893 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
896 b) Assuming that we recompute the advance height of the upper glyph:
901 TSB2 -- +-----+--+ 1 | yadv1 ymax1
903 yadv2 | 0+--+------+ -- --
904 ymax2 | 2 | -- y_offset
909 glyph1: advance height = 6
912 glyph2: advance height = 7
915 TSB is 1 for both glyphs; writing direction is T2B.
919 vertical advance width of glyph2
920 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
921 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
924 Comparing a) with b) shows that b) is easier to compute. I'll wait
925 for a reply from Andrei to see what should really be implemented...
927 Since horizontal advance widths or vertical advance heights
928 can be used alone but not together, no ambiguity occurs. */
930 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &layout_context->info.gpos;
931 hb_codepoint_t last_pos = gpi->last;
932 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
934 /* We don't handle mark glyphs here. */
935 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
938 unsigned int index = (this+coverage) (IN_CURGLYPH ());
939 if (likely (index == NOT_COVERED))
942 const EntryExitRecord &record = entryExitRecord[index];
944 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
947 hb_position_t entry_x, entry_y;
948 (this+record.entryAnchor).get_anchor (layout_context, IN_CURGLYPH (), &entry_x, &entry_y);
952 if (buffer->direction == HB_DIRECTION_RTL)
954 /* advance is absolute, not relative */
955 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
959 /* advance is absolute, not relative */
960 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
963 if (context->lookup_flag & LookupFlag::RightToLeft)
965 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
966 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
970 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
971 POSITION (buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
975 if (record.exitAnchor)
977 gpi->last = buffer->in_pos;
978 (this+record.exitAnchor).get_anchor (layout_context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
985 inline bool sanitize (SANITIZE_ARG_DEF) {
987 return SANITIZE_WITH_BASE (this, coverage)
988 && SANITIZE_WITH_BASE (this, entryExitRecord);
992 USHORT format; /* Format identifier--format = 1 */
994 coverage; /* Offset to Coverage table--from
995 * beginning of subtable */
996 ArrayOf<EntryExitRecord>
997 entryExitRecord; /* Array of EntryExit records--in
998 * Coverage Index order */
1000 ASSERT_SIZE (CursivePosFormat1, 6);
1004 friend struct PosLookupSubTable;
1007 inline bool apply (APPLY_ARG_DEF) const
1011 case 1: return u.format1->apply (APPLY_ARG);
1012 default:return false;
1016 inline bool sanitize (SANITIZE_ARG_DEF) {
1018 if (!SANITIZE (u.format)) return false;
1020 case 1: return u.format1->sanitize (SANITIZE_ARG);
1021 default:return true;
1027 USHORT format; /* Format identifier */
1028 CursivePosFormat1 format1[VAR];
1033 typedef AnchorMatrix BaseArray; /* base-major--
1034 * in order of BaseCoverage Index--,
1036 * ordered by class--zero-based. */
1038 struct MarkBasePosFormat1
1040 friend struct MarkBasePos;
1043 inline bool apply (APPLY_ARG_DEF) const
1046 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1047 if (likely (mark_index == NOT_COVERED))
1050 /* now we search backwards for a non-mark glyph */
1051 unsigned int property;
1052 unsigned int j = buffer->in_pos;
1058 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1061 /* The following assertion is too strong. */
1062 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1066 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1067 if (base_index == NOT_COVERED)
1070 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
1073 inline bool sanitize (SANITIZE_ARG_DEF) {
1075 return SANITIZE_SELF ()
1076 && SANITIZE_WITH_BASE (this, markCoverage)
1077 && SANITIZE_WITH_BASE (this, baseCoverage)
1078 && SANITIZE_WITH_BASE (this, markArray)
1079 && likely (baseArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1083 USHORT format; /* Format identifier--format = 1 */
1085 markCoverage; /* Offset to MarkCoverage table--from
1086 * beginning of MarkBasePos subtable */
1088 baseCoverage; /* Offset to BaseCoverage table--from
1089 * beginning of MarkBasePos subtable */
1090 USHORT classCount; /* Number of classes defined for marks */
1092 markArray; /* Offset to MarkArray table--from
1093 * beginning of MarkBasePos subtable */
1095 baseArray; /* Offset to BaseArray table--from
1096 * beginning of MarkBasePos subtable */
1098 ASSERT_SIZE (MarkBasePosFormat1, 12);
1102 friend struct PosLookupSubTable;
1105 inline bool apply (APPLY_ARG_DEF) const
1109 case 1: return u.format1->apply (APPLY_ARG);
1110 default:return false;
1114 inline bool sanitize (SANITIZE_ARG_DEF) {
1116 if (!SANITIZE (u.format)) return false;
1118 case 1: return u.format1->sanitize (SANITIZE_ARG);
1119 default:return true;
1125 USHORT format; /* Format identifier */
1126 MarkBasePosFormat1 format1[VAR];
1131 typedef AnchorMatrix LigatureAttach; /* component-major--
1132 * in order of writing direction--,
1134 * ordered by class--zero-based. */
1136 typedef OffsetListOf<LigatureAttach> LigatureArray;
1137 /* Array of LigatureAttach
1139 * LigatureCoverage Index */
1141 struct MarkLigPosFormat1
1143 friend struct MarkLigPos;
1146 inline bool apply (APPLY_ARG_DEF) const
1149 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1150 if (likely (mark_index == NOT_COVERED))
1153 /* now we search backwards for a non-mark glyph */
1154 unsigned int property;
1155 unsigned int j = buffer->in_pos;
1161 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1164 /* The following assertion is too strong. */
1165 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1169 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1170 if (lig_index == NOT_COVERED)
1173 const LigatureArray& lig_array = this+ligatureArray;
1174 const LigatureAttach& lig_attach = lig_array[lig_index];
1176 /* Find component to attach to */
1177 unsigned int comp_count = lig_attach.rows;
1178 if (unlikely (!comp_count))
1180 unsigned int comp_index;
1181 /* We must now check whether the ligature ID of the current mark glyph
1182 * is identical to the ligature ID of the found ligature. If yes, we
1183 * can directly use the component index. If not, we attach the mark
1184 * glyph to the last component of the ligature. */
1185 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (buffer->in_pos) && IN_COMPONENT (buffer->in_pos))
1187 comp_index = IN_COMPONENT (buffer->in_pos) - 1;
1188 if (comp_index >= comp_count)
1189 comp_index = comp_count - 1;
1192 comp_index = comp_count - 1;
1194 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1197 inline bool sanitize (SANITIZE_ARG_DEF) {
1199 return SANITIZE_SELF ()
1200 && SANITIZE_WITH_BASE (this, markCoverage)
1201 && SANITIZE_WITH_BASE (this, ligatureCoverage)
1202 && SANITIZE_WITH_BASE (this, markArray)
1203 && likely (ligatureArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1207 USHORT format; /* Format identifier--format = 1 */
1209 markCoverage; /* Offset to Mark Coverage table--from
1210 * beginning of MarkLigPos subtable */
1212 ligatureCoverage; /* Offset to Ligature Coverage
1213 * table--from beginning of MarkLigPos
1215 USHORT classCount; /* Number of defined mark classes */
1217 markArray; /* Offset to MarkArray table--from
1218 * beginning of MarkLigPos subtable */
1219 OffsetTo<LigatureArray>
1220 ligatureArray; /* Offset to LigatureArray table--from
1221 * beginning of MarkLigPos subtable */
1223 ASSERT_SIZE (MarkLigPosFormat1, 12);
1227 friend struct PosLookupSubTable;
1230 inline bool apply (APPLY_ARG_DEF) const
1234 case 1: return u.format1->apply (APPLY_ARG);
1235 default:return false;
1239 inline bool sanitize (SANITIZE_ARG_DEF) {
1241 if (!SANITIZE (u.format)) return false;
1243 case 1: return u.format1->sanitize (SANITIZE_ARG);
1244 default:return true;
1250 USHORT format; /* Format identifier */
1251 MarkLigPosFormat1 format1[VAR];
1256 typedef AnchorMatrix Mark2Array; /* mark2-major--
1257 * in order of Mark2Coverage Index--,
1259 * ordered by class--zero-based. */
1261 struct MarkMarkPosFormat1
1263 friend struct MarkMarkPos;
1266 inline bool apply (APPLY_ARG_DEF) const
1269 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1270 if (likely (mark1_index == NOT_COVERED))
1273 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1274 unsigned int property;
1275 unsigned int j = buffer->in_pos;
1281 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, &property));
1283 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1286 /* Two marks match only if they belong to the same base, or same component
1287 * of the same ligature. That is, the component numbers must match, and
1288 * if those are non-zero, the ligid number should also match. */
1289 if ((IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos)) ||
1290 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (buffer->in_pos)))
1293 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1294 if (mark2_index == NOT_COVERED)
1297 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1300 inline bool sanitize (SANITIZE_ARG_DEF) {
1302 return SANITIZE_SELF ()
1303 && SANITIZE_WITH_BASE (this, mark1Coverage)
1304 && SANITIZE_WITH_BASE (this, mark2Coverage)
1305 && SANITIZE_WITH_BASE (this, mark1Array)
1306 && likely (mark2Array.sanitize (SANITIZE_ARG, CharP(this), classCount));
1310 USHORT format; /* Format identifier--format = 1 */
1312 mark1Coverage; /* Offset to Combining Mark1 Coverage
1313 * table--from beginning of MarkMarkPos
1316 mark2Coverage; /* Offset to Combining Mark2 Coverage
1317 * table--from beginning of MarkMarkPos
1319 USHORT classCount; /* Number of defined mark classes */
1321 mark1Array; /* Offset to Mark1Array table--from
1322 * beginning of MarkMarkPos subtable */
1323 OffsetTo<Mark2Array>
1324 mark2Array; /* Offset to Mark2Array table--from
1325 * beginning of MarkMarkPos subtable */
1327 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1331 friend struct PosLookupSubTable;
1334 inline bool apply (APPLY_ARG_DEF) const
1338 case 1: return u.format1->apply (APPLY_ARG);
1339 default:return false;
1343 inline bool sanitize (SANITIZE_ARG_DEF) {
1345 if (!SANITIZE (u.format)) return false;
1347 case 1: return u.format1->sanitize (SANITIZE_ARG);
1348 default:return true;
1354 USHORT format; /* Format identifier */
1355 MarkMarkPosFormat1 format1[VAR];
1360 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1362 struct ContextPos : Context
1364 friend struct PosLookupSubTable;
1367 inline bool apply (APPLY_ARG_DEF) const
1370 return Context::apply (APPLY_ARG, position_lookup);
1374 struct ChainContextPos : ChainContext
1376 friend struct PosLookupSubTable;
1379 inline bool apply (APPLY_ARG_DEF) const
1382 return ChainContext::apply (APPLY_ARG, position_lookup);
1387 struct ExtensionPos : Extension
1389 friend struct PosLookupSubTable;
1392 inline const struct PosLookupSubTable& get_subtable (void) const
1394 unsigned int offset = get_offset ();
1395 if (unlikely (!offset)) return Null(PosLookupSubTable);
1396 return StructAtOffset<PosLookupSubTable> (*this, offset);
1399 inline bool apply (APPLY_ARG_DEF) const;
1401 inline bool sanitize (SANITIZE_ARG_DEF);
1411 struct PosLookupSubTable
1413 friend struct PosLookup;
1427 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1430 switch (lookup_type) {
1431 case Single: return u.single->apply (APPLY_ARG);
1432 case Pair: return u.pair->apply (APPLY_ARG);
1433 case Cursive: return u.cursive->apply (APPLY_ARG);
1434 case MarkBase: return u.markBase->apply (APPLY_ARG);
1435 case MarkLig: return u.markLig->apply (APPLY_ARG);
1436 case MarkMark: return u.markMark->apply (APPLY_ARG);
1437 case Context: return u.context->apply (APPLY_ARG);
1438 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1439 case Extension: return u.extension->apply (APPLY_ARG);
1440 default:return false;
1444 inline bool sanitize (SANITIZE_ARG_DEF) {
1446 if (!SANITIZE (u.format)) return false;
1448 case Single: return u.single->sanitize (SANITIZE_ARG);
1449 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1450 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1451 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1452 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1453 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1454 case Context: return u.context->sanitize (SANITIZE_ARG);
1455 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1456 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1457 default:return true;
1464 SinglePos single[VAR];
1466 CursivePos cursive[VAR];
1467 MarkBasePos markBase[VAR];
1468 MarkLigPos markLig[VAR];
1469 MarkMarkPos markMark[VAR];
1470 ContextPos context[VAR];
1471 ChainContextPos chainContext[VAR];
1472 ExtensionPos extension[VAR];
1477 struct PosLookup : Lookup
1479 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1480 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1482 inline bool apply_once (hb_ot_layout_context_t *layout_context,
1483 hb_buffer_t *buffer,
1484 unsigned int context_length,
1485 unsigned int nesting_level_left,
1486 unsigned int apply_depth) const
1488 unsigned int lookup_type = get_type ();
1489 hb_apply_context_t context[1];
1491 context->nesting_level_left = nesting_level_left;
1492 context->lookup_flag = get_flag ();
1494 if (!_hb_ot_layout_check_glyph_property (layout_context->face, IN_CURINFO (), context->lookup_flag, &context->property))
1497 for (unsigned int i = 0; i < get_subtable_count (); i++)
1498 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
1504 inline bool apply_string (hb_ot_layout_context_t *layout_context,
1505 hb_buffer_t *buffer,
1506 hb_mask_t mask) const
1510 if (unlikely (!buffer->in_length))
1513 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1516 while (buffer->in_pos < buffer->in_length)
1519 if (~IN_MASK (buffer->in_pos) & mask)
1521 done = apply_once (layout_context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, 0);
1527 /* Contrary to properties defined in GDEF, user-defined properties
1528 will always stop a possible cursive positioning. */
1529 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1539 inline bool sanitize (SANITIZE_ARG_DEF) {
1541 if (unlikely (!Lookup::sanitize (SANITIZE_ARG))) return false;
1542 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1543 return SANITIZE_WITH_BASE (this, list);
1547 typedef OffsetListOf<PosLookup> PosLookupList;
1548 ASSERT_SIZE (PosLookupList, 2);
1554 struct GPOS : GSUBGPOS
1556 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1558 inline const PosLookup& get_lookup (unsigned int i) const
1559 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1561 inline bool position_lookup (hb_ot_layout_context_t *layout_context,
1562 hb_buffer_t *buffer,
1563 unsigned int lookup_index,
1564 hb_mask_t mask) const
1565 { return get_lookup (lookup_index).apply_string (layout_context, buffer, mask); }
1567 inline bool sanitize (SANITIZE_ARG_DEF) {
1569 if (unlikely (!GSUBGPOS::sanitize (SANITIZE_ARG))) return false;
1570 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1571 return SANITIZE_WITH_BASE (this, list);
1574 ASSERT_SIZE (GPOS, 10);
1577 /* Out-of-class implementation for methods recursing */
1579 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1582 return get_subtable ().apply (APPLY_ARG, get_type ());
1585 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1588 if (unlikely (!Extension::sanitize (SANITIZE_ARG))) return false;
1589 unsigned int offset = get_offset ();
1590 if (unlikely (!offset)) return true;
1591 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1594 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1596 const GPOS &gpos = *(layout_context->face->ot_layout.gpos);
1597 const PosLookup &l = gpos.get_lookup (lookup_index);
1599 if (unlikely (context->nesting_level_left == 0))
1602 if (unlikely (context_length < 1))
1605 return l.apply_once (layout_context, buffer, context_length, context->nesting_level_left - 1, apply_depth + 1);
1609 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */