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"
33 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
35 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
39 typedef Value ValueRecord[VAR];
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::static_size; }
90 void apply_value (hb_ot_layout_context_t *layout,
93 hb_internal_glyph_position_t &glyph_pos) const
95 unsigned int x_ppem, y_ppem;
96 unsigned int format = *this;
100 /* design units -> fractional pixel */
101 if (format & xPlacement) glyph_pos.x_offset += layout->scale_x (get_short (values++));
102 if (format & yPlacement) glyph_pos.y_offset += layout->scale_y (get_short (values++));
103 if (format & xAdvance) glyph_pos.x_advance += layout->scale_x (get_short (values++));
104 if (format & yAdvance) glyph_pos.y_advance += layout->scale_y (get_short (values++));
106 if (!has_device ()) return;
108 x_ppem = layout->font->x_ppem;
109 y_ppem = layout->font->y_ppem;
111 if (!x_ppem && !y_ppem) return;
113 /* pixel -> fractional pixel */
114 if (format & xPlaDevice) {
115 if (x_ppem) glyph_pos.x_offset += (base + get_device (values++)).get_delta (x_ppem) * layout->font->x_scale; else values++;
117 if (format & yPlaDevice) {
118 if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_delta (y_ppem) * layout->font->y_scale; else values++;
120 if (format & xAdvDevice) {
121 if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_delta (x_ppem) * layout->font->x_scale; else values++;
123 if (format & yAdvDevice) {
124 if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_delta (y_ppem) * layout->font->y_scale; else values++;
129 inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
130 unsigned int format = *this;
132 if (format & xPlacement) values++;
133 if (format & yPlacement) values++;
134 if (format & xAdvance) values++;
135 if (format & yAdvance) values++;
137 if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
138 if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
139 if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
140 if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
145 static inline OffsetTo<Device>& get_device (Value* value)
146 { return *CastP<OffsetTo<Device> > (value); }
147 static inline const OffsetTo<Device>& get_device (const Value* value)
148 { return *CastP<OffsetTo<Device> > (value); }
150 static inline const SHORT& get_short (const Value* value)
151 { return *CastP<SHORT> (value); }
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 *c, void *base, Value *values) {
162 return c->check_range (values, get_size ())
163 && (!has_device () || sanitize_value_devices (c, base, values));
166 inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
168 unsigned int len = get_len ();
170 if (!c->check_array (values, get_size (), count)) return false;
172 if (!has_device ()) return true;
174 for (unsigned int i = 0; i < count; i++) {
175 if (!sanitize_value_devices (c, base, values))
183 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
184 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
187 if (!has_device ()) return true;
189 for (unsigned int i = 0; i < count; i++) {
190 if (!sanitize_value_devices (c, base, values))
202 friend struct Anchor;
205 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
206 hb_position_t *x, hb_position_t *y) const
208 *x = layout->scale_x (xCoordinate);
209 *y = layout->scale_y (yCoordinate);
212 inline bool sanitize (hb_sanitize_context_t *c) {
214 return c->check_struct (this);
218 USHORT format; /* Format identifier--format = 1 */
219 SHORT xCoordinate; /* Horizontal value--in design units */
220 SHORT yCoordinate; /* Vertical value--in design units */
222 DEFINE_SIZE_STATIC (6);
227 friend struct Anchor;
230 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
231 hb_position_t *x, hb_position_t *y) const
233 unsigned int x_ppem = layout->font->x_ppem;
234 unsigned int y_ppem = layout->font->y_ppem;
235 hb_position_t cx, cy;
238 if (x_ppem || y_ppem)
239 ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
240 *x = x_ppem && ret ? cx : layout->scale_x (xCoordinate);
241 *y = y_ppem && ret ? cy : layout->scale_y (yCoordinate);
244 inline bool sanitize (hb_sanitize_context_t *c) {
246 return c->check_struct (this);
250 USHORT format; /* Format identifier--format = 2 */
251 SHORT xCoordinate; /* Horizontal value--in design units */
252 SHORT yCoordinate; /* Vertical value--in design units */
253 USHORT anchorPoint; /* Index to glyph contour point */
255 DEFINE_SIZE_STATIC (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 = layout->scale_x (xCoordinate);
267 *y = layout->scale_y (yCoordinate);
269 /* pixel -> fractional pixel */
270 if (layout->font->x_ppem)
271 *x += (this+xDeviceTable).get_delta (layout->font->x_ppem) * layout->font->x_scale;
272 if (layout->font->y_ppem)
273 *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) * layout->font->y_scale;
276 inline bool sanitize (hb_sanitize_context_t *c) {
278 return c->check_struct (this)
279 && xDeviceTable.sanitize (c, this)
280 && yDeviceTable.sanitize (c, this);
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 DEFINE_SIZE_STATIC (10);
301 inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
302 hb_position_t *x, hb_position_t *y) const
306 case 1: u.format1.get_anchor (layout, glyph_id, x, y); return;
307 case 2: u.format2.get_anchor (layout, glyph_id, x, y); return;
308 case 3: u.format3.get_anchor (layout, glyph_id, x, y); return;
313 inline bool sanitize (hb_sanitize_context_t *c) {
315 if (!u.format.sanitize (c)) return false;
317 case 1: return u.format1.sanitize (c);
318 case 2: return u.format2.sanitize (c);
319 case 3: return u.format3.sanitize (c);
326 USHORT format; /* Format identifier */
327 AnchorFormat1 format1;
328 AnchorFormat2 format2;
329 AnchorFormat3 format3;
332 DEFINE_SIZE_UNION (2, format);
338 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
339 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
340 return this+matrix[row * cols + col];
343 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
345 if (!c->check_struct (this)) return false;
346 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
347 unsigned int count = rows * cols;
348 if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
349 for (unsigned int i = 0; i < count; i++)
350 if (!matrix[i].sanitize (c, this)) return false;
354 USHORT rows; /* Number of rows */
357 matrix[VAR]; /* Matrix of offsets to Anchor tables--
358 * from beginning of AnchorMatrix table */
360 DEFINE_SIZE_ARRAY (2, matrix);
366 friend struct MarkArray;
368 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
370 return c->check_struct (this)
371 && markAnchor.sanitize (c, base);
375 USHORT klass; /* Class defined for this mark */
377 markAnchor; /* Offset to Anchor table--from
378 * beginning of MarkArray table */
380 DEFINE_SIZE_STATIC (4);
383 struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */
385 inline bool apply (hb_apply_context_t *c,
386 unsigned int mark_index, unsigned int glyph_index,
387 const AnchorMatrix &anchors, unsigned int class_count,
388 unsigned int glyph_pos) const
391 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
392 unsigned int mark_class = record.klass;
394 const Anchor& mark_anchor = this + record.markAnchor;
395 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
397 hb_position_t mark_x, mark_y, base_x, base_y;
399 mark_anchor.get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &mark_x, &mark_y);
400 glyph_anchor.get_anchor (c->layout, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
402 hb_internal_glyph_position_t &o = c->buffer->pos[c->buffer->i];
405 o.x_offset = base_x - mark_x;
406 o.y_offset = base_y - mark_y;
407 o.back = c->buffer->i - glyph_pos;
413 inline bool sanitize (hb_sanitize_context_t *c) {
415 return ArrayOf<MarkRecord>::sanitize (c, this);
422 struct SinglePosFormat1
424 friend struct SinglePos;
427 inline bool apply (hb_apply_context_t *c) const
430 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
431 if (likely (index == NOT_COVERED))
434 valueFormat.apply_value (c->layout, this, values, c->buffer->pos[c->buffer->i]);
440 inline bool sanitize (hb_sanitize_context_t *c) {
442 return c->check_struct (this)
443 && coverage.sanitize (c, this)
444 && valueFormat.sanitize_value (c, this, values);
448 USHORT format; /* Format identifier--format = 1 */
450 coverage; /* Offset to Coverage table--from
451 * beginning of subtable */
452 ValueFormat valueFormat; /* Defines the types of data in the
454 ValueRecord values; /* Defines positioning
455 * value(s)--applied to all glyphs in
456 * the Coverage table */
458 DEFINE_SIZE_ARRAY (6, values);
461 struct SinglePosFormat2
463 friend struct SinglePos;
466 inline bool apply (hb_apply_context_t *c) const
469 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
470 if (likely (index == NOT_COVERED))
473 if (likely (index >= valueCount))
476 valueFormat.apply_value (c->layout, this,
477 &values[index * valueFormat.get_len ()],
478 c->buffer->pos[c->buffer->i]);
484 inline bool sanitize (hb_sanitize_context_t *c) {
486 return c->check_struct (this)
487 && coverage.sanitize (c, this)
488 && valueFormat.sanitize_values (c, this, values, valueCount);
492 USHORT format; /* Format identifier--format = 2 */
494 coverage; /* Offset to Coverage table--from
495 * beginning of subtable */
496 ValueFormat valueFormat; /* Defines the types of data in the
498 USHORT valueCount; /* Number of ValueRecords */
499 ValueRecord values; /* Array of ValueRecords--positioning
500 * values applied to glyphs */
502 DEFINE_SIZE_ARRAY (8, values);
507 friend struct PosLookupSubTable;
510 inline bool apply (hb_apply_context_t *c) const
514 case 1: return u.format1.apply (c);
515 case 2: return u.format2.apply (c);
516 default:return false;
520 inline bool sanitize (hb_sanitize_context_t *c) {
522 if (!u.format.sanitize (c)) return false;
524 case 1: return u.format1.sanitize (c);
525 case 2: return u.format2.sanitize (c);
532 USHORT format; /* Format identifier */
533 SinglePosFormat1 format1;
534 SinglePosFormat2 format2;
539 struct PairValueRecord
541 friend struct PairSet;
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 DEFINE_SIZE_ARRAY (2, values);
555 friend struct PairPosFormat1;
557 inline bool apply (hb_apply_context_t *c,
558 const ValueFormat *valueFormats,
559 unsigned int pos) const
562 unsigned int len1 = valueFormats[0].get_len ();
563 unsigned int len2 = valueFormats[1].get_len ();
564 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
566 unsigned int count = len;
567 const PairValueRecord *record = CastP<PairValueRecord> (array);
568 for (unsigned int i = 0; i < count; i++)
570 if (c->buffer->info[pos].codepoint == record->secondGlyph)
572 valueFormats[0].apply_value (c->layout, this, &record->values[0], c->buffer->pos[c->buffer->i]);
573 valueFormats[1].apply_value (c->layout, this, &record->values[len1], c->buffer->pos[pos]);
579 record = &StructAtOffset<PairValueRecord> (record, record_size);
585 struct sanitize_closure_t {
587 ValueFormat *valueFormats;
588 unsigned int len1; /* valueFormats[0].get_len() */
589 unsigned int stride; /* 1 + len1 + len2 */
592 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
594 if (!(c->check_struct (this)
595 && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
597 unsigned int count = len;
598 PairValueRecord *record = CastP<PairValueRecord> (array);
599 return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
600 && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
604 USHORT len; /* Number of PairValueRecords */
605 USHORT array[VAR]; /* Array of PairValueRecords--ordered
606 * by GlyphID of the second glyph */
608 DEFINE_SIZE_ARRAY (2, array);
611 struct PairPosFormat1
613 friend struct PairPos;
616 inline bool apply (hb_apply_context_t *c) const
619 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
620 if (unlikely (c->buffer->i + 2 > end))
623 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
624 if (likely (index == NOT_COVERED))
627 unsigned int j = c->buffer->i + 1;
628 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
630 if (unlikely (j == end))
635 return (this+pairSet[index]).apply (c, &valueFormat1, j);
638 inline bool sanitize (hb_sanitize_context_t *c) {
641 unsigned int len1 = valueFormat1.get_len ();
642 unsigned int len2 = valueFormat2.get_len ();
643 PairSet::sanitize_closure_t closure = {
650 return c->check_struct (this)
651 && coverage.sanitize (c, this)
652 && pairSet.sanitize (c, this, &closure);
656 USHORT format; /* Format identifier--format = 1 */
658 coverage; /* Offset to Coverage table--from
659 * beginning of subtable */
660 ValueFormat valueFormat1; /* Defines the types of data in
661 * ValueRecord1--for the first glyph
662 * in the pair--may be zero (0) */
663 ValueFormat valueFormat2; /* Defines the types of data in
664 * ValueRecord2--for the second glyph
665 * in the pair--may be zero (0) */
666 OffsetArrayOf<PairSet>
667 pairSet; /* Array of PairSet tables
668 * ordered by Coverage Index */
670 DEFINE_SIZE_ARRAY (10, pairSet);
673 struct PairPosFormat2
675 friend struct PairPos;
678 inline bool apply (hb_apply_context_t *c) const
681 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
682 if (unlikely (c->buffer->i + 2 > end))
685 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
686 if (likely (index == NOT_COVERED))
689 unsigned int j = c->buffer->i + 1;
690 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
692 if (unlikely (j == end))
697 unsigned int len1 = valueFormat1.get_len ();
698 unsigned int len2 = valueFormat2.get_len ();
699 unsigned int record_len = len1 + len2;
701 unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->i].codepoint);
702 unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
703 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
706 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
707 valueFormat1.apply_value (c->layout, this, v, c->buffer->pos[c->buffer->i]);
708 valueFormat2.apply_value (c->layout, this, v + len1, c->buffer->pos[j]);
717 inline bool sanitize (hb_sanitize_context_t *c) {
719 if (!(c->check_struct (this)
720 && coverage.sanitize (c, this)
721 && classDef1.sanitize (c, this)
722 && classDef2.sanitize (c, this))) return false;
724 unsigned int len1 = valueFormat1.get_len ();
725 unsigned int len2 = valueFormat2.get_len ();
726 unsigned int stride = len1 + len2;
727 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
728 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
729 return c->check_array (values, record_size, count) &&
730 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
731 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
735 USHORT format; /* Format identifier--format = 2 */
737 coverage; /* Offset to Coverage table--from
738 * beginning of subtable */
739 ValueFormat valueFormat1; /* ValueRecord definition--for the
740 * first glyph of the pair--may be zero
742 ValueFormat valueFormat2; /* ValueRecord definition--for the
743 * second glyph of the pair--may be
746 classDef1; /* Offset to ClassDef table--from
747 * beginning of PairPos subtable--for
748 * the first glyph of the pair */
750 classDef2; /* Offset to ClassDef table--from
751 * beginning of PairPos subtable--for
752 * the second glyph of the pair */
753 USHORT class1Count; /* Number of classes in ClassDef1
754 * table--includes Class0 */
755 USHORT class2Count; /* Number of classes in ClassDef2
756 * table--includes Class0 */
757 ValueRecord values; /* Matrix of value pairs:
758 * class1-major, class2-minor,
759 * Each entry has value1 and value2 */
761 DEFINE_SIZE_ARRAY (16, values);
766 friend struct PosLookupSubTable;
769 inline bool apply (hb_apply_context_t *c) const
773 case 1: return u.format1.apply (c);
774 case 2: return u.format2.apply (c);
775 default:return false;
779 inline bool sanitize (hb_sanitize_context_t *c) {
781 if (!u.format.sanitize (c)) return false;
783 case 1: return u.format1.sanitize (c);
784 case 2: return u.format2.sanitize (c);
791 USHORT format; /* Format identifier */
792 PairPosFormat1 format1;
793 PairPosFormat2 format2;
798 struct EntryExitRecord
800 friend struct CursivePosFormat1;
802 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
804 return entryAnchor.sanitize (c, base)
805 && exitAnchor.sanitize (c, base);
810 entryAnchor; /* Offset to EntryAnchor table--from
811 * beginning of CursivePos
812 * subtable--may be NULL */
814 exitAnchor; /* Offset to ExitAnchor table--from
815 * beginning of CursivePos
816 * subtable--may be NULL */
818 DEFINE_SIZE_STATIC (4);
821 struct CursivePosFormat1
823 friend struct CursivePos;
826 inline bool apply (hb_apply_context_t *c) const
829 /* Now comes the messiest part of the whole OpenType
830 specification. At first glance, cursive connections seem easy
831 to understand, but there are pitfalls! The reason is that
832 the specs don't mention how to compute the advance values
833 resp. glyph offsets. I was told it would be an omission, to
834 be fixed in the next OpenType version... Again many thanks to
835 Andrei Burago <andreib@microsoft.com> for clarifications.
837 Consider the following example:
850 glyph1: advance width = 12
853 glyph2: advance width = 11
856 LSB is 1 for both glyphs (so the boxes drawn above are glyph
857 bboxes). Writing direction is R2L; `0' denotes the glyph's
860 Now the surprising part: The advance width of the *left* glyph
861 (resp. of the *bottom* glyph) will be modified, no matter
862 whether the writing direction is L2R or R2L (resp. T2B or
863 B2T)! This assymetry is caused by the fact that the glyph's
864 coordinate origin is always the lower left corner for all
867 Continuing the above example, we can compute the new
868 (horizontal) advance width of glyph2 as
872 and the new vertical offset of glyph2 as
877 Vertical writing direction is far more complicated:
879 a) Assuming that we recompute the advance height of the lower glyph:
886 yadv2 | 0+--+------+ -- BSB1 --
889 BSB2 -- 0+--------+ --
892 glyph1: advance height = 6
895 glyph2: advance height = 7
898 TSB is 1 for both glyphs; writing direction is T2B.
901 BSB1 = yadv1 - (TSB1 + ymax1)
902 BSB2 = yadv2 - (TSB2 + ymax2)
905 vertical advance width of glyph2
906 = y_offset + BSB2 - BSB1
907 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
908 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
909 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
912 b) Assuming that we recompute the advance height of the upper glyph:
917 TSB2 -- +-----+--+ 1 | yadv1 ymax1
919 yadv2 | 0+--+------+ -- --
920 ymax2 | 2 | -- y_offset
925 glyph1: advance height = 6
928 glyph2: advance height = 7
931 TSB is 1 for both glyphs; writing direction is T2B.
935 vertical advance width of glyph2
936 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
937 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
940 Comparing a) with b) shows that b) is easier to compute. I'll wait
941 for a reply from Andrei to see what should really be implemented...
943 Since horizontal advance widths or vertical advance heights
944 can be used alone but not together, no ambiguity occurs. */
946 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &c->layout->info.gpos;
947 hb_codepoint_t last_pos = gpi->last;
948 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
950 /* We don't handle mark glyphs here. */
951 if (c->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
954 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
955 if (likely (index == NOT_COVERED))
958 const EntryExitRecord &record = entryExitRecord[index];
960 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
963 hb_position_t entry_x, entry_y;
964 (this+record.entryAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &entry_x, &entry_y);
968 if (c->buffer->direction == HB_DIRECTION_RTL)
970 /* advance is absolute, not relative */
971 c->buffer->pos[c->buffer->i].x_advance = entry_x - gpi->anchor_x;
975 /* advance is absolute, not relative */
976 c->buffer->pos[last_pos].x_advance = gpi->anchor_x - entry_x;
979 if (c->lookup_flag & LookupFlag::RightToLeft)
981 c->buffer->pos[last_pos].cursive_chain = last_pos - c->buffer->i;
982 c->buffer->pos[last_pos].y_offset = entry_y - gpi->anchor_y;
986 c->buffer->pos[c->buffer->i].cursive_chain = c->buffer->i - last_pos;
987 c->buffer->pos[c->buffer->i].y_offset = gpi->anchor_y - entry_y;
991 if (record.exitAnchor)
993 gpi->last = c->buffer->i;
994 (this+record.exitAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &gpi->anchor_x, &gpi->anchor_y);
1001 inline bool sanitize (hb_sanitize_context_t *c) {
1003 return coverage.sanitize (c, this)
1004 && entryExitRecord.sanitize (c, this);
1008 USHORT format; /* Format identifier--format = 1 */
1010 coverage; /* Offset to Coverage table--from
1011 * beginning of subtable */
1012 ArrayOf<EntryExitRecord>
1013 entryExitRecord; /* Array of EntryExit records--in
1014 * Coverage Index order */
1016 DEFINE_SIZE_ARRAY (6, entryExitRecord);
1021 friend struct PosLookupSubTable;
1024 inline bool apply (hb_apply_context_t *c) const
1028 case 1: return u.format1.apply (c);
1029 default:return false;
1033 inline bool sanitize (hb_sanitize_context_t *c) {
1035 if (!u.format.sanitize (c)) return false;
1037 case 1: return u.format1.sanitize (c);
1038 default:return true;
1044 USHORT format; /* Format identifier */
1045 CursivePosFormat1 format1;
1050 typedef AnchorMatrix BaseArray; /* base-major--
1051 * in order of BaseCoverage Index--,
1053 * ordered by class--zero-based. */
1055 struct MarkBasePosFormat1
1057 friend struct MarkBasePos;
1060 inline bool apply (hb_apply_context_t *c) const
1063 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1064 if (likely (mark_index == NOT_COVERED))
1067 /* now we search backwards for a non-mark glyph */
1068 unsigned int property;
1069 unsigned int j = c->buffer->i;
1075 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1077 /* The following assertion is too strong, so we've disabled it. */
1078 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1081 unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
1082 if (base_index == NOT_COVERED)
1085 return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
1088 inline bool sanitize (hb_sanitize_context_t *c) {
1090 return c->check_struct (this)
1091 && markCoverage.sanitize (c, this)
1092 && baseCoverage.sanitize (c, this)
1093 && markArray.sanitize (c, this)
1094 && baseArray.sanitize (c, this, (unsigned int) classCount);
1098 USHORT format; /* Format identifier--format = 1 */
1100 markCoverage; /* Offset to MarkCoverage table--from
1101 * beginning of MarkBasePos subtable */
1103 baseCoverage; /* Offset to BaseCoverage table--from
1104 * beginning of MarkBasePos subtable */
1105 USHORT classCount; /* Number of classes defined for marks */
1107 markArray; /* Offset to MarkArray table--from
1108 * beginning of MarkBasePos subtable */
1110 baseArray; /* Offset to BaseArray table--from
1111 * beginning of MarkBasePos subtable */
1113 DEFINE_SIZE_STATIC (12);
1118 friend struct PosLookupSubTable;
1121 inline bool apply (hb_apply_context_t *c) const
1125 case 1: return u.format1.apply (c);
1126 default:return false;
1130 inline bool sanitize (hb_sanitize_context_t *c) {
1132 if (!u.format.sanitize (c)) return false;
1134 case 1: return u.format1.sanitize (c);
1135 default:return true;
1141 USHORT format; /* Format identifier */
1142 MarkBasePosFormat1 format1;
1147 typedef AnchorMatrix LigatureAttach; /* component-major--
1148 * in order of writing direction--,
1150 * ordered by class--zero-based. */
1152 typedef OffsetListOf<LigatureAttach> LigatureArray;
1153 /* Array of LigatureAttach
1155 * LigatureCoverage Index */
1157 struct MarkLigPosFormat1
1159 friend struct MarkLigPos;
1162 inline bool apply (hb_apply_context_t *c) const
1165 unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1166 if (likely (mark_index == NOT_COVERED))
1169 /* now we search backwards for a non-mark glyph */
1170 unsigned int property;
1171 unsigned int j = c->buffer->i;
1177 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1179 /* The following assertion is too strong, so we've disabled it. */
1180 if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1183 unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1184 if (lig_index == NOT_COVERED)
1187 const LigatureArray& lig_array = this+ligatureArray;
1188 const LigatureAttach& lig_attach = lig_array[lig_index];
1190 /* Find component to attach to */
1191 unsigned int comp_count = lig_attach.rows;
1192 if (unlikely (!comp_count))
1194 unsigned int comp_index;
1195 /* We must now check whether the ligature ID of the current mark glyph
1196 * is identical to the ligature ID of the found ligature. If yes, we
1197 * can directly use the component index. If not, we attach the mark
1198 * glyph to the last component of the ligature. */
1199 if (c->buffer->info[j].lig_id && c->buffer->info[j].lig_id == c->buffer->info[c->buffer->i].lig_id && c->buffer->info[c->buffer->i].component)
1201 comp_index = c->buffer->info[c->buffer->i].component - 1;
1202 if (comp_index >= comp_count)
1203 comp_index = comp_count - 1;
1206 comp_index = comp_count - 1;
1208 return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1211 inline bool sanitize (hb_sanitize_context_t *c) {
1213 return c->check_struct (this)
1214 && markCoverage.sanitize (c, this)
1215 && ligatureCoverage.sanitize (c, this)
1216 && markArray.sanitize (c, this)
1217 && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1221 USHORT format; /* Format identifier--format = 1 */
1223 markCoverage; /* Offset to Mark Coverage table--from
1224 * beginning of MarkLigPos subtable */
1226 ligatureCoverage; /* Offset to Ligature Coverage
1227 * table--from beginning of MarkLigPos
1229 USHORT classCount; /* Number of defined mark classes */
1231 markArray; /* Offset to MarkArray table--from
1232 * beginning of MarkLigPos subtable */
1233 OffsetTo<LigatureArray>
1234 ligatureArray; /* Offset to LigatureArray table--from
1235 * beginning of MarkLigPos subtable */
1237 DEFINE_SIZE_STATIC (12);
1242 friend struct PosLookupSubTable;
1245 inline bool apply (hb_apply_context_t *c) const
1249 case 1: return u.format1.apply (c);
1250 default:return false;
1254 inline bool sanitize (hb_sanitize_context_t *c) {
1256 if (!u.format.sanitize (c)) return false;
1258 case 1: return u.format1.sanitize (c);
1259 default:return true;
1265 USHORT format; /* Format identifier */
1266 MarkLigPosFormat1 format1;
1271 typedef AnchorMatrix Mark2Array; /* mark2-major--
1272 * in order of Mark2Coverage Index--,
1274 * ordered by class--zero-based. */
1276 struct MarkMarkPosFormat1
1278 friend struct MarkMarkPos;
1281 inline bool apply (hb_apply_context_t *c) const
1284 unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
1285 if (likely (mark1_index == NOT_COVERED))
1288 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1289 unsigned int property;
1290 unsigned int j = c->buffer->i;
1296 } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property));
1298 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1301 /* Two marks match only if they belong to the same base, or same component
1302 * of the same ligature. That is, the component numbers must match, and
1303 * if those are non-zero, the ligid number should also match. */
1304 if ((c->buffer->info[j].component != c->buffer->info[c->buffer->i].component) ||
1305 (c->buffer->info[j].component && c->buffer->info[j].lig_id != c->buffer->info[c->buffer->i].lig_id))
1308 unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1309 if (mark2_index == NOT_COVERED)
1312 return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1315 inline bool sanitize (hb_sanitize_context_t *c) {
1317 return c->check_struct (this)
1318 && mark1Coverage.sanitize (c, this)
1319 && mark2Coverage.sanitize (c, this)
1320 && mark1Array.sanitize (c, this)
1321 && mark2Array.sanitize (c, this, (unsigned int) classCount);
1325 USHORT format; /* Format identifier--format = 1 */
1327 mark1Coverage; /* Offset to Combining Mark1 Coverage
1328 * table--from beginning of MarkMarkPos
1331 mark2Coverage; /* Offset to Combining Mark2 Coverage
1332 * table--from beginning of MarkMarkPos
1334 USHORT classCount; /* Number of defined mark classes */
1336 mark1Array; /* Offset to Mark1Array table--from
1337 * beginning of MarkMarkPos subtable */
1338 OffsetTo<Mark2Array>
1339 mark2Array; /* Offset to Mark2Array table--from
1340 * beginning of MarkMarkPos subtable */
1342 DEFINE_SIZE_STATIC (12);
1347 friend struct PosLookupSubTable;
1350 inline bool apply (hb_apply_context_t *c) const
1354 case 1: return u.format1.apply (c);
1355 default:return false;
1359 inline bool sanitize (hb_sanitize_context_t *c) {
1361 if (!u.format.sanitize (c)) return false;
1363 case 1: return u.format1.sanitize (c);
1364 default:return true;
1370 USHORT format; /* Format identifier */
1371 MarkMarkPosFormat1 format1;
1376 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1378 struct ContextPos : Context
1380 friend struct PosLookupSubTable;
1383 inline bool apply (hb_apply_context_t *c) const
1386 return Context::apply (c, position_lookup);
1390 struct ChainContextPos : ChainContext
1392 friend struct PosLookupSubTable;
1395 inline bool apply (hb_apply_context_t *c) const
1398 return ChainContext::apply (c, position_lookup);
1403 struct ExtensionPos : Extension
1405 friend struct PosLookupSubTable;
1408 inline const struct PosLookupSubTable& get_subtable (void) const
1410 unsigned int offset = get_offset ();
1411 if (unlikely (!offset)) return Null(PosLookupSubTable);
1412 return StructAtOffset<PosLookupSubTable> (this, offset);
1415 inline bool apply (hb_apply_context_t *c) const;
1417 inline bool sanitize (hb_sanitize_context_t *c);
1427 struct PosLookupSubTable
1429 friend struct PosLookup;
1443 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1446 switch (lookup_type) {
1447 case Single: return u.single.apply (c);
1448 case Pair: return u.pair.apply (c);
1449 case Cursive: return u.cursive.apply (c);
1450 case MarkBase: return u.markBase.apply (c);
1451 case MarkLig: return u.markLig.apply (c);
1452 case MarkMark: return u.markMark.apply (c);
1453 case Context: return u.c.apply (c);
1454 case ChainContext: return u.chainContext.apply (c);
1455 case Extension: return u.extension.apply (c);
1456 default:return false;
1460 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1462 switch (lookup_type) {
1463 case Single: return u.single.sanitize (c);
1464 case Pair: return u.pair.sanitize (c);
1465 case Cursive: return u.cursive.sanitize (c);
1466 case MarkBase: return u.markBase.sanitize (c);
1467 case MarkLig: return u.markLig.sanitize (c);
1468 case MarkMark: return u.markMark.sanitize (c);
1469 case Context: return u.c.sanitize (c);
1470 case ChainContext: return u.chainContext.sanitize (c);
1471 case Extension: return u.extension.sanitize (c);
1472 default:return true;
1482 MarkBasePos markBase;
1484 MarkMarkPos markMark;
1486 ChainContextPos chainContext;
1487 ExtensionPos extension;
1490 DEFINE_SIZE_UNION (2, sub_format);
1494 struct PosLookup : Lookup
1496 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1497 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1499 inline bool apply_once (hb_ot_layout_context_t *layout,
1500 hb_buffer_t *buffer,
1501 unsigned int context_length,
1502 unsigned int nesting_level_left) const
1504 unsigned int lookup_type = get_type ();
1505 hb_apply_context_t c[1] = {{0}};
1509 c->context_length = context_length;
1510 c->nesting_level_left = nesting_level_left;
1511 c->lookup_flag = get_flag ();
1513 if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
1516 for (unsigned int i = 0; i < get_subtable_count (); i++)
1517 if (get_subtable (i).apply (c, lookup_type))
1523 inline bool apply_string (hb_ot_layout_context_t *layout,
1524 hb_buffer_t *buffer,
1525 hb_mask_t mask) const
1529 if (unlikely (!buffer->len))
1532 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1535 while (buffer->i < buffer->len)
1538 if (~buffer->info[buffer->i].mask & mask)
1540 done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1546 /* Contrary to properties defined in GDEF, user-defined properties
1547 will always stop a possible cursive positioning. */
1548 layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1558 inline bool sanitize (hb_sanitize_context_t *c) {
1560 if (unlikely (!Lookup::sanitize (c))) return false;
1561 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1562 return list.sanitize (c, this, get_type ());
1566 typedef OffsetListOf<PosLookup> PosLookupList;
1572 struct GPOS : GSUBGPOS
1574 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1576 inline const PosLookup& get_lookup (unsigned int i) const
1577 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1579 inline bool position_lookup (hb_ot_layout_context_t *layout,
1580 hb_buffer_t *buffer,
1581 unsigned int lookup_index,
1582 hb_mask_t mask) const
1583 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1585 inline bool sanitize (hb_sanitize_context_t *c) {
1587 if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1588 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1589 return list.sanitize (c, this);
1592 DEFINE_SIZE_STATIC (10);
1596 /* Out-of-class implementation for methods recursing */
1598 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1601 return get_subtable ().apply (c, get_type ());
1604 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1607 if (unlikely (!Extension::sanitize (c))) return false;
1608 unsigned int offset = get_offset ();
1609 if (unlikely (!offset)) return true;
1610 return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1613 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1615 const GPOS &gpos = *(c->layout->face->ot_layout->gpos);
1616 const PosLookup &l = gpos.get_lookup (lookup_index);
1618 if (unlikely (c->nesting_level_left == 0))
1621 if (unlikely (c->context_length < 1))
1624 return l.apply_once (c->layout, c->buffer, c->context_length, c->nesting_level_left - 1);
1628 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */