2 * Copyright (C) 2007,2008,2009,2010 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::static_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, get_short (values++));
110 if (format & yPlacement) glyph_pos->y_offset += _hb_16dot16_mul_round (y_scale, get_short (values++));
111 if (format & xAdvance) glyph_pos->x_advance += _hb_16dot16_mul_round (x_scale, get_short (values++));
112 if (format & yAdvance) glyph_pos->y_advance += _hb_16dot16_mul_round (y_scale, get_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 + get_device (values++)).get_delta (x_ppem) << 16; else values++;
125 if (format & yPlaDevice) {
126 if (y_ppem) glyph_pos->y_offset += (base + get_device (values++)).get_delta (y_ppem) << 16; else values++;
128 if (format & xAdvDevice) {
129 if (x_ppem) glyph_pos->x_advance += (base + get_device (values++)).get_delta (x_ppem) << 16; else values++;
131 if (format & yAdvDevice) {
132 if (y_ppem) glyph_pos->y_advance += (base + get_device (values++)).get_delta (y_ppem) << 16; else values++;
137 inline bool sanitize_value_devices (hb_sanitize_context_t *context, void *base, 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) && !get_device (values++).sanitize (context, base)) return false;
146 if ((format & yPlaDevice) && !get_device (values++).sanitize (context, base)) return false;
147 if ((format & xAdvDevice) && !get_device (values++).sanitize (context, base)) return false;
148 if ((format & yAdvDevice) && !get_device (values++).sanitize (context, base)) return false;
153 static inline OffsetTo<Device>& get_device (Value* value)
154 { return *CastP<OffsetTo<Device> > (value); }
155 static inline const OffsetTo<Device>& get_device (const Value* value)
156 { return *CastP<OffsetTo<Device> > (value); }
158 static inline const SHORT& get_short (const Value* value)
159 { return *CastP<SHORT> (value); }
163 inline bool has_device () const {
164 unsigned int format = *this;
165 return (format & devices) != 0;
168 inline bool sanitize_value (hb_sanitize_context_t *context, void *base, Value *values) {
170 return context->check_range (values, get_size ())
171 && (!has_device () || sanitize_value_devices (context, base, values));
174 inline bool sanitize_values (hb_sanitize_context_t *context, void *base, Value *values, unsigned int count) {
176 unsigned int len = get_len ();
178 if (!context->check_array (values, get_size (), count)) return false;
180 if (!has_device ()) return true;
182 for (unsigned int i = 0; i < count; i++) {
183 if (!sanitize_value_devices (context, base, values))
191 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
192 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *context, void *base, Value *values, unsigned int count, unsigned int stride) {
195 if (!has_device ()) return true;
197 for (unsigned int i = 0; i < count; i++) {
198 if (!sanitize_value_devices (context, base, values))
206 ASSERT_SIZE (ValueFormat, 2);
211 friend struct Anchor;
214 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
215 hb_position_t *x, hb_position_t *y) const
217 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
218 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
221 inline bool sanitize (hb_sanitize_context_t *context) {
223 return context->check_struct (this);
227 USHORT format; /* Format identifier--format = 1 */
228 SHORT xCoordinate; /* Horizontal value--in design units */
229 SHORT yCoordinate; /* Vertical value--in design units */
231 ASSERT_SIZE (AnchorFormat1, 6);
235 friend struct Anchor;
238 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
239 hb_position_t *x, hb_position_t *y) const
241 unsigned int x_ppem = layout->font->x_ppem;
242 unsigned int y_ppem = layout->font->y_ppem;
243 hb_position_t cx, cy;
246 if (x_ppem || y_ppem)
247 ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
248 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
249 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
252 inline bool sanitize (hb_sanitize_context_t *context) {
254 return context->check_struct (this);
258 USHORT format; /* Format identifier--format = 2 */
259 SHORT xCoordinate; /* Horizontal value--in design units */
260 SHORT yCoordinate; /* Vertical value--in design units */
261 USHORT anchorPoint; /* Index to glyph contour point */
263 ASSERT_SIZE (AnchorFormat2, 8);
267 friend struct Anchor;
270 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
271 hb_position_t *x, hb_position_t *y) const
273 *x = _hb_16dot16_mul_round (layout->font->x_scale, xCoordinate);
274 *y = _hb_16dot16_mul_round (layout->font->y_scale, yCoordinate);
276 /* pixel -> fractional pixel */
277 if (layout->font->x_ppem)
278 *x += (this+xDeviceTable).get_delta (layout->font->x_ppem) << 16;
279 if (layout->font->y_ppem)
280 *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) << 16;
283 inline bool sanitize (hb_sanitize_context_t *context) {
285 return context->check_struct (this)
286 && xDeviceTable.sanitize (context, this)
287 && yDeviceTable.sanitize (context, this);
291 USHORT format; /* Format identifier--format = 3 */
292 SHORT xCoordinate; /* Horizontal value--in design units */
293 SHORT yCoordinate; /* Vertical value--in design units */
295 xDeviceTable; /* Offset to Device table for X
296 * coordinate-- from beginning of
297 * Anchor table (may be NULL) */
299 yDeviceTable; /* Offset to Device table for Y
300 * coordinate-- from beginning of
301 * Anchor table (may be NULL) */
303 ASSERT_SIZE (AnchorFormat3, 10);
307 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
308 hb_position_t *x, hb_position_t *y) const
312 case 1: u.format1->get_anchor (layout, glyph_id, x, y); return;
313 case 2: u.format2->get_anchor (layout, glyph_id, x, y); return;
314 case 3: u.format3->get_anchor (layout, glyph_id, x, y); return;
319 inline bool sanitize (hb_sanitize_context_t *context) {
321 if (!u.format.sanitize (context)) return false;
323 case 1: return u.format1->sanitize (context);
324 case 2: return u.format2->sanitize (context);
325 case 3: return u.format3->sanitize (context);
332 USHORT format; /* Format identifier */
333 AnchorFormat1 format1[VAR];
334 AnchorFormat2 format2[VAR];
335 AnchorFormat3 format3[VAR];
342 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
343 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
344 return this+matrix[row * cols + col];
347 inline bool sanitize (hb_sanitize_context_t *context, unsigned int cols) {
349 if (!context->check_struct (this)) return false;
350 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
351 unsigned int count = rows * cols;
352 if (!context->check_array (matrix, matrix[0].static_size, count)) return false;
353 for (unsigned int i = 0; i < count; i++)
354 if (!matrix[i].sanitize (context, this)) return false;
358 USHORT rows; /* Number of rows */
361 matrix[VAR]; /* Matrix of offsets to Anchor tables--
362 * from beginning of AnchorMatrix table */
364 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
369 friend struct MarkArray;
371 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
373 return context->check_struct (this)
374 && markAnchor.sanitize (context, base);
377 DEFINE_SIZE_STATIC (4);
380 USHORT klass; /* Class defined for this mark */
382 markAnchor; /* Offset to Anchor table--from
383 * beginning of MarkArray table */
388 inline bool apply (hb_apply_context_t *context,
389 unsigned int mark_index, unsigned int glyph_index,
390 const AnchorMatrix &anchors, unsigned int class_count,
391 unsigned int glyph_pos) const
394 const MarkRecord &record = markRecord[mark_index];
395 unsigned int mark_class = record.klass;
397 const Anchor& mark_anchor = this + record.markAnchor;
398 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
400 hb_position_t mark_x, mark_y, base_x, base_y;
402 mark_anchor.get_anchor (context->layout, IN_CURGLYPH (), &mark_x, &mark_y);
403 glyph_anchor.get_anchor (context->layout, IN_GLYPH (glyph_pos), &base_x, &base_y);
405 hb_internal_glyph_position_t *o = POSITION (context->buffer->in_pos);
408 o->x_offset = base_x - mark_x;
409 o->y_offset = base_y - mark_y;
410 o->back = context->buffer->in_pos - glyph_pos;
412 context->buffer->in_pos++;
416 inline bool sanitize (hb_sanitize_context_t *context) {
418 return markRecord.sanitize (context, this);
423 markRecord; /* Array of MarkRecords--in Coverage order */
425 ASSERT_SIZE (MarkArray, 2);
430 struct SinglePosFormat1
432 friend struct SinglePos;
435 inline bool apply (hb_apply_context_t *context) const
438 unsigned int index = (this+coverage) (IN_CURGLYPH ());
439 if (likely (index == NOT_COVERED))
442 valueFormat.apply_value (context->layout, CharP(this), values, CURPOSITION ());
444 context->buffer->in_pos++;
448 inline bool sanitize (hb_sanitize_context_t *context) {
450 return context->check_struct (this)
451 && coverage.sanitize (context, this)
452 && valueFormat.sanitize_value (context, CharP(this), values);
456 USHORT format; /* Format identifier--format = 1 */
458 coverage; /* Offset to Coverage table--from
459 * beginning of subtable */
460 ValueFormat valueFormat; /* Defines the types of data in the
462 ValueRecord values; /* Defines positioning
463 * value(s)--applied to all glyphs in
464 * the Coverage table */
466 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
468 struct SinglePosFormat2
470 friend struct SinglePos;
473 inline bool apply (hb_apply_context_t *context) const
476 unsigned int index = (this+coverage) (IN_CURGLYPH ());
477 if (likely (index == NOT_COVERED))
480 if (likely (index >= valueCount))
483 valueFormat.apply_value (context->layout, CharP(this),
484 &values[index * valueFormat.get_len ()],
487 context->buffer->in_pos++;
491 inline bool sanitize (hb_sanitize_context_t *context) {
493 return context->check_struct (this)
494 && coverage.sanitize (context, this)
495 && valueFormat.sanitize_values (context, CharP(this), values, valueCount);
499 USHORT format; /* Format identifier--format = 2 */
501 coverage; /* Offset to Coverage table--from
502 * beginning of subtable */
503 ValueFormat valueFormat; /* Defines the types of data in the
505 USHORT valueCount; /* Number of ValueRecords */
506 ValueRecord values; /* Array of ValueRecords--positioning
507 * values applied to glyphs */
509 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
513 friend struct PosLookupSubTable;
516 inline bool apply (hb_apply_context_t *context) const
520 case 1: return u.format1->apply (context);
521 case 2: return u.format2->apply (context);
522 default:return false;
526 inline bool sanitize (hb_sanitize_context_t *context) {
528 if (!u.format.sanitize (context)) return false;
530 case 1: return u.format1->sanitize (context);
531 case 2: return u.format2->sanitize (context);
538 USHORT format; /* Format identifier */
539 SinglePosFormat1 format1[VAR];
540 SinglePosFormat2 format2[VAR];
545 struct PairValueRecord
547 friend struct PairPosFormat1;
550 GlyphID secondGlyph; /* GlyphID of second glyph in the
551 * pair--first glyph is listed in the
553 ValueRecord values; /* Positioning data for the first glyph
554 * followed by for second glyph */
556 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
560 friend struct PairPosFormat1;
562 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
563 inline bool sanitize (hb_sanitize_context_t *context, unsigned int format_len) {
565 if (!context->check_struct (this)) return false;
566 unsigned int count = (1 + format_len) * len;
567 return context->check_array (array, USHORT::static_size, count);
571 USHORT len; /* Number of PairValueRecords */
573 array[VAR]; /* Array of PairValueRecords--ordered
574 * by GlyphID of the second glyph */
576 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
578 struct PairPosFormat1
580 friend struct PairPos;
583 inline bool apply (hb_apply_context_t *context) const
586 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
587 if (unlikely (context->buffer->in_pos + 2 > end))
590 unsigned int index = (this+coverage) (IN_CURGLYPH ());
591 if (likely (index == NOT_COVERED))
594 unsigned int j = context->buffer->in_pos + 1;
595 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
597 if (unlikely (j == end))
602 unsigned int len1 = valueFormat1.get_len ();
603 unsigned int len2 = valueFormat2.get_len ();
604 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
606 const PairSet &pair_set = this+pairSet[index];
607 unsigned int count = pair_set.len;
608 const PairValueRecord *record = pair_set.array;
609 for (unsigned int i = 0; i < count; i++)
611 if (IN_GLYPH (j) == record->secondGlyph)
613 valueFormat1.apply_value (context->layout, CharP(this), &record->values[0], CURPOSITION ());
614 valueFormat2.apply_value (context->layout, CharP(this), &record->values[len1], POSITION (j));
617 context->buffer->in_pos = j;
620 record = &StructAtOffset<PairValueRecord> (*record, record_size);
626 inline bool sanitize (hb_sanitize_context_t *context) {
629 unsigned int len1 = valueFormat1.get_len ();
630 unsigned int len2 = valueFormat2.get_len ();
632 if (!(context->check_struct (this)
633 && coverage.sanitize (context, this)
634 && likely (pairSet.sanitize (context, CharP(this), len1 + len2)))) return false;
636 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
638 unsigned int stride = 1 + len1 + len2;
639 unsigned int count1 = pairSet.len;
640 for (unsigned int i = 0; i < count1; i++)
642 PairSet &pair_set = const_cast<PairSet &> (this+pairSet[i]); /* XXX clean this up */
644 unsigned int count2 = pair_set.len;
645 PairValueRecord *record = pair_set.array;
646 if (!(valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &record->values[0], count2, stride) &&
647 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &record->values[len1], count2, stride)))
655 USHORT format; /* Format identifier--format = 1 */
657 coverage; /* Offset to Coverage table--from
658 * beginning of subtable */
659 ValueFormat valueFormat1; /* Defines the types of data in
660 * ValueRecord1--for the first glyph
661 * in the pair--may be zero (0) */
662 ValueFormat valueFormat2; /* Defines the types of data in
663 * ValueRecord2--for the second glyph
664 * in the pair--may be zero (0) */
665 OffsetArrayOf<PairSet>
666 pairSet; /* Array of PairSet tables
667 * ordered by Coverage Index */
669 ASSERT_SIZE (PairPosFormat1, 10);
671 struct PairPosFormat2
673 friend struct PairPos;
676 inline bool apply (hb_apply_context_t *context) const
679 unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
680 if (unlikely (context->buffer->in_pos + 2 > end))
683 unsigned int index = (this+coverage) (IN_CURGLYPH ());
684 if (likely (index == NOT_COVERED))
687 unsigned int j = context->buffer->in_pos + 1;
688 while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
690 if (unlikely (j == end))
695 unsigned int len1 = valueFormat1.get_len ();
696 unsigned int len2 = valueFormat2.get_len ();
697 unsigned int record_len = len1 + len2;
699 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
700 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
701 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
704 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
705 valueFormat1.apply_value (context->layout, CharP(this), v, CURPOSITION ());
706 valueFormat2.apply_value (context->layout, CharP(this), v + len1, POSITION (j));
710 context->buffer->in_pos = j;
715 inline bool sanitize (hb_sanitize_context_t *context) {
717 if (!(context->check_struct (this)
718 && coverage.sanitize (context, this)
719 && classDef1.sanitize (context, this)
720 && classDef2.sanitize (context, this))) return false;
722 unsigned int len1 = valueFormat1.get_len ();
723 unsigned int len2 = valueFormat2.get_len ();
724 unsigned int stride = len1 + len2;
725 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
726 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
727 return context->check_array (values, record_size, count) &&
728 valueFormat1.sanitize_values_stride_unsafe (context, CharP(this), &values[0], count, stride) &&
729 valueFormat2.sanitize_values_stride_unsafe (context, CharP(this), &values[len1], count, stride);
733 USHORT format; /* Format identifier--format = 2 */
735 coverage; /* Offset to Coverage table--from
736 * beginning of subtable */
737 ValueFormat valueFormat1; /* ValueRecord definition--for the
738 * first glyph of the pair--may be zero
740 ValueFormat valueFormat2; /* ValueRecord definition--for the
741 * second glyph of the pair--may be
744 classDef1; /* Offset to ClassDef table--from
745 * beginning of PairPos subtable--for
746 * the first glyph of the pair */
748 classDef2; /* Offset to ClassDef table--from
749 * beginning of PairPos subtable--for
750 * the second glyph of the pair */
751 USHORT class1Count; /* Number of classes in ClassDef1
752 * table--includes Class0 */
753 USHORT class2Count; /* Number of classes in ClassDef2
754 * table--includes Class0 */
755 ValueRecord values; /* Matrix of value pairs:
756 * class1-major, class2-minor,
757 * Each entry has value1 and value2 */
759 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
763 friend struct PosLookupSubTable;
766 inline bool apply (hb_apply_context_t *context) const
770 case 1: return u.format1->apply (context);
771 case 2: return u.format2->apply (context);
772 default:return false;
776 inline bool sanitize (hb_sanitize_context_t *context) {
778 if (!u.format.sanitize (context)) return false;
780 case 1: return u.format1->sanitize (context);
781 case 2: return u.format2->sanitize (context);
788 USHORT format; /* Format identifier */
789 PairPosFormat1 format1[VAR];
790 PairPosFormat2 format2[VAR];
795 struct EntryExitRecord
797 inline bool sanitize (hb_sanitize_context_t *context, void *base) {
799 return entryAnchor.sanitize (context, base)
800 && exitAnchor.sanitize (context, base);
803 DEFINE_SIZE_STATIC (4);
806 entryAnchor; /* Offset to EntryAnchor table--from
807 * beginning of CursivePos
808 * subtable--may be NULL */
810 exitAnchor; /* Offset to ExitAnchor table--from
811 * beginning of CursivePos
812 * subtable--may be NULL */
815 struct CursivePosFormat1
817 friend struct CursivePos;
820 inline bool apply (hb_apply_context_t *context) const
823 /* Now comes the messiest part of the whole OpenType
824 specification. At first glance, cursive connections seem easy
825 to understand, but there are pitfalls! The reason is that
826 the specs don't mention how to compute the advance values
827 resp. glyph offsets. I was told it would be an omission, to
828 be fixed in the next OpenType version... Again many thanks to
829 Andrei Burago <andreib@microsoft.com> for clarifications.
831 Consider the following example:
844 glyph1: advance width = 12
847 glyph2: advance width = 11
850 LSB is 1 for both glyphs (so the boxes drawn above are glyph
851 bboxes). Writing direction is R2L; `0' denotes the glyph's
854 Now the surprising part: The advance width of the *left* glyph
855 (resp. of the *bottom* glyph) will be modified, no matter
856 whether the writing direction is L2R or R2L (resp. T2B or
857 B2T)! This assymetry is caused by the fact that the glyph's
858 coordinate origin is always the lower left corner for all
861 Continuing the above example, we can compute the new
862 (horizontal) advance width of glyph2 as
866 and the new vertical offset of glyph2 as
871 Vertical writing direction is far more complicated:
873 a) Assuming that we recompute the advance height of the lower glyph:
880 yadv2 | 0+--+------+ -- BSB1 --
883 BSB2 -- 0+--------+ --
886 glyph1: advance height = 6
889 glyph2: advance height = 7
892 TSB is 1 for both glyphs; writing direction is T2B.
895 BSB1 = yadv1 - (TSB1 + ymax1)
896 BSB2 = yadv2 - (TSB2 + ymax2)
899 vertical advance width of glyph2
900 = y_offset + BSB2 - BSB1
901 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
902 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
903 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
906 b) Assuming that we recompute the advance height of the upper glyph:
911 TSB2 -- +-----+--+ 1 | yadv1 ymax1
913 yadv2 | 0+--+------+ -- --
914 ymax2 | 2 | -- y_offset
919 glyph1: advance height = 6
922 glyph2: advance height = 7
925 TSB is 1 for both glyphs; writing direction is T2B.
929 vertical advance width of glyph2
930 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
931 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
934 Comparing a) with b) shows that b) is easier to compute. I'll wait
935 for a reply from Andrei to see what should really be implemented...
937 Since horizontal advance widths or vertical advance heights
938 can be used alone but not together, no ambiguity occurs. */
940 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->layout->info.gpos;
941 hb_codepoint_t last_pos = gpi->last;
942 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
944 /* We don't handle mark glyphs here. */
945 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
948 unsigned int index = (this+coverage) (IN_CURGLYPH ());
949 if (likely (index == NOT_COVERED))
952 const EntryExitRecord &record = entryExitRecord[index];
954 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
957 hb_position_t entry_x, entry_y;
958 (this+record.entryAnchor).get_anchor (context->layout, IN_CURGLYPH (), &entry_x, &entry_y);
962 if (context->buffer->direction == HB_DIRECTION_RTL)
964 /* advance is absolute, not relative */
965 POSITION (context->buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
969 /* advance is absolute, not relative */
970 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
973 if (context->lookup_flag & LookupFlag::RightToLeft)
975 POSITION (last_pos)->cursive_chain = last_pos - context->buffer->in_pos;
976 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
980 POSITION (context->buffer->in_pos)->cursive_chain = context->buffer->in_pos - last_pos;
981 POSITION (context->buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
985 if (record.exitAnchor)
987 gpi->last = context->buffer->in_pos;
988 (this+record.exitAnchor).get_anchor (context->layout, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
991 context->buffer->in_pos++;
995 inline bool sanitize (hb_sanitize_context_t *context) {
997 return coverage.sanitize (context, this)
998 && entryExitRecord.sanitize (context, this);
1002 USHORT format; /* Format identifier--format = 1 */
1004 coverage; /* Offset to Coverage table--from
1005 * beginning of subtable */
1006 ArrayOf<EntryExitRecord>
1007 entryExitRecord; /* Array of EntryExit records--in
1008 * Coverage Index order */
1010 ASSERT_SIZE (CursivePosFormat1, 6);
1014 friend struct PosLookupSubTable;
1017 inline bool apply (hb_apply_context_t *context) const
1021 case 1: return u.format1->apply (context);
1022 default:return false;
1026 inline bool sanitize (hb_sanitize_context_t *context) {
1028 if (!u.format.sanitize (context)) return false;
1030 case 1: return u.format1->sanitize (context);
1031 default:return true;
1037 USHORT format; /* Format identifier */
1038 CursivePosFormat1 format1[VAR];
1043 typedef AnchorMatrix BaseArray; /* base-major--
1044 * in order of BaseCoverage Index--,
1046 * ordered by class--zero-based. */
1048 struct MarkBasePosFormat1
1050 friend struct MarkBasePos;
1053 inline bool apply (hb_apply_context_t *context) const
1056 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1057 if (likely (mark_index == NOT_COVERED))
1060 /* now we search backwards for a non-mark glyph */
1061 unsigned int property;
1062 unsigned int j = context->buffer->in_pos;
1068 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1071 /* The following assertion is too strong. */
1072 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1076 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1077 if (base_index == NOT_COVERED)
1080 return (this+markArray).apply (context, mark_index, base_index, this+baseArray, classCount, j);
1083 inline bool sanitize (hb_sanitize_context_t *context) {
1085 return context->check_struct (this)
1086 && markCoverage.sanitize (context, this)
1087 && baseCoverage.sanitize (context, this)
1088 && markArray.sanitize (context, this)
1089 && likely (baseArray.sanitize (context, CharP(this), (unsigned int) classCount));
1093 USHORT format; /* Format identifier--format = 1 */
1095 markCoverage; /* Offset to MarkCoverage table--from
1096 * beginning of MarkBasePos subtable */
1098 baseCoverage; /* Offset to BaseCoverage table--from
1099 * beginning of MarkBasePos subtable */
1100 USHORT classCount; /* Number of classes defined for marks */
1102 markArray; /* Offset to MarkArray table--from
1103 * beginning of MarkBasePos subtable */
1105 baseArray; /* Offset to BaseArray table--from
1106 * beginning of MarkBasePos subtable */
1108 ASSERT_SIZE (MarkBasePosFormat1, 12);
1112 friend struct PosLookupSubTable;
1115 inline bool apply (hb_apply_context_t *context) const
1119 case 1: return u.format1->apply (context);
1120 default:return false;
1124 inline bool sanitize (hb_sanitize_context_t *context) {
1126 if (!u.format.sanitize (context)) return false;
1128 case 1: return u.format1->sanitize (context);
1129 default:return true;
1135 USHORT format; /* Format identifier */
1136 MarkBasePosFormat1 format1[VAR];
1141 typedef AnchorMatrix LigatureAttach; /* component-major--
1142 * in order of writing direction--,
1144 * ordered by class--zero-based. */
1146 typedef OffsetListOf<LigatureAttach> LigatureArray;
1147 /* Array of LigatureAttach
1149 * LigatureCoverage Index */
1151 struct MarkLigPosFormat1
1153 friend struct MarkLigPos;
1156 inline bool apply (hb_apply_context_t *context) const
1159 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1160 if (likely (mark_index == NOT_COVERED))
1163 /* now we search backwards for a non-mark glyph */
1164 unsigned int property;
1165 unsigned int j = context->buffer->in_pos;
1171 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1174 /* The following assertion is too strong. */
1175 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1179 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1180 if (lig_index == NOT_COVERED)
1183 const LigatureArray& lig_array = this+ligatureArray;
1184 const LigatureAttach& lig_attach = lig_array[lig_index];
1186 /* Find component to attach to */
1187 unsigned int comp_count = lig_attach.rows;
1188 if (unlikely (!comp_count))
1190 unsigned int comp_index;
1191 /* We must now check whether the ligature ID of the current mark glyph
1192 * is identical to the ligature ID of the found ligature. If yes, we
1193 * can directly use the component index. If not, we attach the mark
1194 * glyph to the last component of the ligature. */
1195 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (context->buffer->in_pos) && IN_COMPONENT (context->buffer->in_pos))
1197 comp_index = IN_COMPONENT (context->buffer->in_pos) - 1;
1198 if (comp_index >= comp_count)
1199 comp_index = comp_count - 1;
1202 comp_index = comp_count - 1;
1204 return (this+markArray).apply (context, mark_index, comp_index, lig_attach, classCount, j);
1207 inline bool sanitize (hb_sanitize_context_t *context) {
1209 return context->check_struct (this)
1210 && markCoverage.sanitize (context, this)
1211 && ligatureCoverage.sanitize (context, this)
1212 && markArray.sanitize (context, this)
1213 && likely (ligatureArray.sanitize (context, CharP(this), (unsigned int) classCount));
1217 USHORT format; /* Format identifier--format = 1 */
1219 markCoverage; /* Offset to Mark Coverage table--from
1220 * beginning of MarkLigPos subtable */
1222 ligatureCoverage; /* Offset to Ligature Coverage
1223 * table--from beginning of MarkLigPos
1225 USHORT classCount; /* Number of defined mark classes */
1227 markArray; /* Offset to MarkArray table--from
1228 * beginning of MarkLigPos subtable */
1229 OffsetTo<LigatureArray>
1230 ligatureArray; /* Offset to LigatureArray table--from
1231 * beginning of MarkLigPos subtable */
1233 ASSERT_SIZE (MarkLigPosFormat1, 12);
1237 friend struct PosLookupSubTable;
1240 inline bool apply (hb_apply_context_t *context) const
1244 case 1: return u.format1->apply (context);
1245 default:return false;
1249 inline bool sanitize (hb_sanitize_context_t *context) {
1251 if (!u.format.sanitize (context)) return false;
1253 case 1: return u.format1->sanitize (context);
1254 default:return true;
1260 USHORT format; /* Format identifier */
1261 MarkLigPosFormat1 format1[VAR];
1266 typedef AnchorMatrix Mark2Array; /* mark2-major--
1267 * in order of Mark2Coverage Index--,
1269 * ordered by class--zero-based. */
1271 struct MarkMarkPosFormat1
1273 friend struct MarkMarkPos;
1276 inline bool apply (hb_apply_context_t *context) const
1279 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1280 if (likely (mark1_index == NOT_COVERED))
1283 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1284 unsigned int property;
1285 unsigned int j = context->buffer->in_pos;
1291 } while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, &property));
1293 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1296 /* Two marks match only if they belong to the same base, or same component
1297 * of the same ligature. That is, the component numbers must match, and
1298 * if those are non-zero, the ligid number should also match. */
1299 if ((IN_COMPONENT (j) != IN_COMPONENT (context->buffer->in_pos)) ||
1300 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (context->buffer->in_pos)))
1303 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1304 if (mark2_index == NOT_COVERED)
1307 return (this+mark1Array).apply (context, mark1_index, mark2_index, this+mark2Array, classCount, j);
1310 inline bool sanitize (hb_sanitize_context_t *context) {
1312 return context->check_struct (this)
1313 && mark1Coverage.sanitize (context, this)
1314 && mark2Coverage.sanitize (context, this)
1315 && mark1Array.sanitize (context, this)
1316 && likely (mark2Array.sanitize (context, CharP(this), (unsigned int) classCount));
1320 USHORT format; /* Format identifier--format = 1 */
1322 mark1Coverage; /* Offset to Combining Mark1 Coverage
1323 * table--from beginning of MarkMarkPos
1326 mark2Coverage; /* Offset to Combining Mark2 Coverage
1327 * table--from beginning of MarkMarkPos
1329 USHORT classCount; /* Number of defined mark classes */
1331 mark1Array; /* Offset to Mark1Array table--from
1332 * beginning of MarkMarkPos subtable */
1333 OffsetTo<Mark2Array>
1334 mark2Array; /* Offset to Mark2Array table--from
1335 * beginning of MarkMarkPos subtable */
1337 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1341 friend struct PosLookupSubTable;
1344 inline bool apply (hb_apply_context_t *context) const
1348 case 1: return u.format1->apply (context);
1349 default:return false;
1353 inline bool sanitize (hb_sanitize_context_t *context) {
1355 if (!u.format.sanitize (context)) return false;
1357 case 1: return u.format1->sanitize (context);
1358 default:return true;
1364 USHORT format; /* Format identifier */
1365 MarkMarkPosFormat1 format1[VAR];
1370 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index);
1372 struct ContextPos : Context
1374 friend struct PosLookupSubTable;
1377 inline bool apply (hb_apply_context_t *context) const
1380 return Context::apply (context, position_lookup);
1384 struct ChainContextPos : ChainContext
1386 friend struct PosLookupSubTable;
1389 inline bool apply (hb_apply_context_t *context) const
1392 return ChainContext::apply (context, position_lookup);
1397 struct ExtensionPos : Extension
1399 friend struct PosLookupSubTable;
1402 inline const struct PosLookupSubTable& get_subtable (void) const
1404 unsigned int offset = get_offset ();
1405 if (unlikely (!offset)) return Null(PosLookupSubTable);
1406 return StructAtOffset<PosLookupSubTable> (*this, offset);
1409 inline bool apply (hb_apply_context_t *context) const;
1411 inline bool sanitize (hb_sanitize_context_t *context);
1421 struct PosLookupSubTable
1423 friend struct PosLookup;
1437 inline bool apply (hb_apply_context_t *context, unsigned int lookup_type) const
1440 switch (lookup_type) {
1441 case Single: return u.single->apply (context);
1442 case Pair: return u.pair->apply (context);
1443 case Cursive: return u.cursive->apply (context);
1444 case MarkBase: return u.markBase->apply (context);
1445 case MarkLig: return u.markLig->apply (context);
1446 case MarkMark: return u.markMark->apply (context);
1447 case Context: return u.context->apply (context);
1448 case ChainContext: return u.chainContext->apply (context);
1449 case Extension: return u.extension->apply (context);
1450 default:return false;
1454 inline bool sanitize (hb_sanitize_context_t *context) {
1456 if (!u.format.sanitize (context)) return false;
1458 case Single: return u.single->sanitize (context);
1459 case Pair: return u.pair->sanitize (context);
1460 case Cursive: return u.cursive->sanitize (context);
1461 case MarkBase: return u.markBase->sanitize (context);
1462 case MarkLig: return u.markLig->sanitize (context);
1463 case MarkMark: return u.markMark->sanitize (context);
1464 case Context: return u.context->sanitize (context);
1465 case ChainContext: return u.chainContext->sanitize (context);
1466 case Extension: return u.extension->sanitize (context);
1467 default:return true;
1474 SinglePos single[VAR];
1476 CursivePos cursive[VAR];
1477 MarkBasePos markBase[VAR];
1478 MarkLigPos markLig[VAR];
1479 MarkMarkPos markMark[VAR];
1480 ContextPos context[VAR];
1481 ChainContextPos chainContext[VAR];
1482 ExtensionPos extension[VAR];
1487 struct PosLookup : Lookup
1489 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1490 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1492 inline bool apply_once (hb_ot_layout_context_t *layout,
1493 hb_buffer_t *buffer,
1494 unsigned int context_length,
1495 unsigned int nesting_level_left) const
1497 unsigned int lookup_type = get_type ();
1498 hb_apply_context_t context[1] = {{0}};
1500 context->layout = layout;
1501 context->buffer = buffer;
1502 context->context_length = context_length;
1503 context->nesting_level_left = nesting_level_left;
1504 context->lookup_flag = get_flag ();
1506 if (!_hb_ot_layout_check_glyph_property (context->layout->face, IN_CURINFO (), context->lookup_flag, &context->property))
1509 for (unsigned int i = 0; i < get_subtable_count (); i++)
1510 if (get_subtable (i).apply (context, lookup_type))
1516 inline bool apply_string (hb_ot_layout_context_t *layout,
1517 hb_buffer_t *buffer,
1518 hb_mask_t mask) const
1521 #define BUFFER buffer
1524 if (unlikely (!buffer->in_length))
1527 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1530 while (buffer->in_pos < buffer->in_length)
1533 if (~IN_MASK (buffer->in_pos) & mask)
1535 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1541 /* Contrary to properties defined in GDEF, user-defined properties
1542 will always stop a possible cursive positioning. */
1543 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1553 inline bool sanitize (hb_sanitize_context_t *context) {
1555 if (unlikely (!Lookup::sanitize (context))) return false;
1556 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1557 return list.sanitize (context, this);
1561 typedef OffsetListOf<PosLookup> PosLookupList;
1562 ASSERT_SIZE (PosLookupList, 2);
1568 struct GPOS : GSUBGPOS
1570 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1572 inline const PosLookup& get_lookup (unsigned int i) const
1573 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1575 inline bool position_lookup (hb_ot_layout_context_t *layout,
1576 hb_buffer_t *buffer,
1577 unsigned int lookup_index,
1578 hb_mask_t mask) const
1579 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1581 inline bool sanitize (hb_sanitize_context_t *context) {
1583 if (unlikely (!GSUBGPOS::sanitize (context))) return false;
1584 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1585 return list.sanitize (context, this);
1588 ASSERT_SIZE (GPOS, 10);
1591 /* Out-of-class implementation for methods recursing */
1593 inline bool ExtensionPos::apply (hb_apply_context_t *context) const
1596 return get_subtable ().apply (context, get_type ());
1599 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *context)
1602 if (unlikely (!Extension::sanitize (context))) return false;
1603 unsigned int offset = get_offset ();
1604 if (unlikely (!offset)) return true;
1605 return StructAtOffset<PosLookupSubTable> (*this, offset).sanitize (context);
1608 static inline bool position_lookup (hb_apply_context_t *context, unsigned int lookup_index)
1610 const GPOS &gpos = *(context->layout->face->ot_layout.gpos);
1611 const PosLookup &l = gpos.get_lookup (lookup_index);
1613 if (unlikely (context->nesting_level_left == 0))
1616 if (unlikely (context->context_length < 1))
1619 return l.apply_once (context->layout, context->buffer, context->context_length, context->nesting_level_left - 1);
1623 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */