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 *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 = context->font->x_scale;
102 y_scale = 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 x_ppem = context->font->x_ppem;
110 y_ppem = context->font->y_ppem;
111 /* pixel -> fractional pixel */
112 if (format & xPlaDevice) {
113 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
115 if (format & yPlaDevice) {
116 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
118 if (format & xAdvDevice) {
119 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
121 if (format & yAdvDevice) {
122 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
127 inline bool sanitize_value_devices (SANITIZE_ARG_DEF, void *base, const Value *values) {
128 unsigned int format = *this;
130 if (format & xPlacement) values++;
131 if (format & yPlacement) values++;
132 if (format & xAdvance) values++;
133 if (format & yAdvance) values++;
135 if ((format & xPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
136 if ((format & yPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
137 if ((format & xAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
138 if ((format & yAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
145 inline bool has_device () {
146 unsigned int format = *this;
147 return (format & devices) != 0;
150 inline bool sanitize_value (SANITIZE_ARG_DEF, void *base, const Value *values) {
153 return SANITIZE_MEM (values, get_size ()) &&
154 (!has_device () || sanitize_value_devices (SANITIZE_ARG, base, values));
157 inline bool sanitize_values (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count) {
159 unsigned int len = get_len ();
161 if (!SANITIZE_ARRAY (values, get_size (), count)) return false;
163 if (!has_device ()) return true;
165 for (unsigned int i = 0; i < count; i++) {
166 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
174 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
175 inline bool sanitize_values_stride_unsafe (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count, unsigned int stride) {
178 if (!has_device ()) return true;
180 for (unsigned int i = 0; i < count; i++) {
181 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
189 ASSERT_SIZE (ValueFormat, 2);
194 friend struct Anchor;
197 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id HB_GNUC_UNUSED,
198 hb_position_t *x, hb_position_t *y) const
200 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
201 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
204 inline bool sanitize (SANITIZE_ARG_DEF) {
206 return SANITIZE_SELF ();
210 USHORT format; /* Format identifier--format = 1 */
211 SHORT xCoordinate; /* Horizontal value--in design units */
212 SHORT yCoordinate; /* Vertical value--in design units */
214 ASSERT_SIZE (AnchorFormat1, 6);
218 friend struct Anchor;
221 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
222 hb_position_t *x, hb_position_t *y) const
225 * NOTE only adjust directions with nonzero ppem */
226 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
227 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
230 inline bool sanitize (SANITIZE_ARG_DEF) {
232 return SANITIZE_SELF ();
236 USHORT format; /* Format identifier--format = 2 */
237 SHORT xCoordinate; /* Horizontal value--in design units */
238 SHORT yCoordinate; /* Vertical value--in design units */
239 USHORT anchorPoint; /* Index to glyph contour point */
241 ASSERT_SIZE (AnchorFormat2, 8);
245 friend struct Anchor;
248 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id HB_GNUC_UNUSED,
249 hb_position_t *x, hb_position_t *y) const
251 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
252 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
254 /* pixel -> fractional pixel */
255 if (context->font->x_ppem)
256 *x += (this+xDeviceTable).get_delta (context->font->x_ppem) << 16;
257 if (context->font->y_ppem)
258 *y += (this+yDeviceTable).get_delta (context->font->y_ppem) << 16;
261 inline bool sanitize (SANITIZE_ARG_DEF) {
263 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
267 USHORT format; /* Format identifier--format = 3 */
268 SHORT xCoordinate; /* Horizontal value--in design units */
269 SHORT yCoordinate; /* Vertical value--in design units */
271 xDeviceTable; /* Offset to Device table for X
272 * coordinate-- from beginning of
273 * Anchor table (may be NULL) */
275 yDeviceTable; /* Offset to Device table for Y
276 * coordinate-- from beginning of
277 * Anchor table (may be NULL) */
279 ASSERT_SIZE (AnchorFormat3, 10);
283 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
284 hb_position_t *x, hb_position_t *y) const
288 case 1: u.format1->get_anchor (context, glyph_id, x, y); return;
289 case 2: u.format2->get_anchor (context, glyph_id, x, y); return;
290 case 3: u.format3->get_anchor (context, glyph_id, x, y); return;
295 inline bool sanitize (SANITIZE_ARG_DEF) {
297 if (!SANITIZE (u.format)) return false;
299 case 1: return u.format1->sanitize (SANITIZE_ARG);
300 case 2: return u.format2->sanitize (SANITIZE_ARG);
301 case 3: return u.format3->sanitize (SANITIZE_ARG);
308 USHORT format; /* Format identifier */
309 AnchorFormat1 format1[VAR];
310 AnchorFormat2 format2[VAR];
311 AnchorFormat3 format3[VAR];
318 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
319 if (HB_UNLIKELY (row >= rows || col >= cols)) return Null(Anchor);
320 return this+matrix[row * cols + col];
323 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
325 if (!SANITIZE_SELF ()) return false;
326 if (HB_UNLIKELY (cols >= ((unsigned int) -1) / rows)) return false;
327 unsigned int count = rows * cols;
328 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
329 for (unsigned int i = 0; i < count; i++)
330 if (!SANITIZE_THIS (matrix[i])) return false;
334 USHORT rows; /* Number of rows */
337 matrix[VAR]; /* Matrix of offsets to Anchor tables--
338 * from beginning of AnchorMatrix table */
340 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
345 friend struct MarkArray;
347 static inline unsigned int get_size () { return sizeof (MarkRecord); }
349 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
351 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
355 USHORT klass; /* Class defined for this mark */
357 markAnchor; /* Offset to Anchor table--from
358 * beginning of MarkArray table */
360 ASSERT_SIZE (MarkRecord, 4);
364 inline bool apply (APPLY_ARG_DEF,
365 unsigned int mark_index, unsigned int glyph_index,
366 const AnchorMatrix &anchors, unsigned int class_count,
367 unsigned int glyph_pos) const
370 const MarkRecord &record = markRecord[mark_index];
371 unsigned int mark_class = record.klass;
373 const Anchor& mark_anchor = this + record.markAnchor;
374 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
376 hb_position_t mark_x, mark_y, base_x, base_y;
378 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
379 glyph_anchor.get_anchor (context, IN_GLYPH (glyph_pos), &base_x, &base_y);
381 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
384 o->x_offset = base_x - mark_x;
385 o->y_offset = base_y - mark_y;
386 o->back = buffer->in_pos - glyph_pos;
392 inline bool sanitize (SANITIZE_ARG_DEF) {
394 return SANITIZE_THIS (markRecord);
399 markRecord; /* Array of MarkRecords--in Coverage order */
401 ASSERT_SIZE (MarkArray, 2);
406 struct SinglePosFormat1
408 friend struct SinglePos;
411 inline bool apply (APPLY_ARG_DEF) const
414 unsigned int index = (this+coverage) (IN_CURGLYPH ());
415 if (HB_LIKELY (index == NOT_COVERED))
418 valueFormat.apply_value (context, CharP(this), values, CURPOSITION ());
424 inline bool sanitize (SANITIZE_ARG_DEF) {
426 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
427 valueFormat.sanitize_value (SANITIZE_ARG, CharP(this), values);
431 USHORT format; /* Format identifier--format = 1 */
433 coverage; /* Offset to Coverage table--from
434 * beginning of subtable */
435 ValueFormat valueFormat; /* Defines the types of data in the
437 ValueRecord values; /* Defines positioning
438 * value(s)--applied to all glyphs in
439 * the Coverage table */
441 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
443 struct SinglePosFormat2
445 friend struct SinglePos;
448 inline bool apply (APPLY_ARG_DEF) const
451 unsigned int index = (this+coverage) (IN_CURGLYPH ());
452 if (HB_LIKELY (index == NOT_COVERED))
455 if (HB_LIKELY (index >= valueCount))
458 valueFormat.apply_value (context, CharP(this),
459 &values[index * valueFormat.get_len ()],
466 inline bool sanitize (SANITIZE_ARG_DEF) {
468 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
469 valueFormat.sanitize_values (SANITIZE_ARG, CharP(this), values, valueCount);
473 USHORT format; /* Format identifier--format = 2 */
475 coverage; /* Offset to Coverage table--from
476 * beginning of subtable */
477 ValueFormat valueFormat; /* Defines the types of data in the
479 USHORT valueCount; /* Number of ValueRecords */
480 ValueRecord values; /* Array of ValueRecords--positioning
481 * values applied to glyphs */
483 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
487 friend struct PosLookupSubTable;
490 inline bool apply (APPLY_ARG_DEF) const
494 case 1: return u.format1->apply (APPLY_ARG);
495 case 2: return u.format2->apply (APPLY_ARG);
496 default:return false;
500 inline bool sanitize (SANITIZE_ARG_DEF) {
502 if (!SANITIZE (u.format)) return false;
504 case 1: return u.format1->sanitize (SANITIZE_ARG);
505 case 2: return u.format2->sanitize (SANITIZE_ARG);
512 USHORT format; /* Format identifier */
513 SinglePosFormat1 format1[VAR];
514 SinglePosFormat2 format2[VAR];
519 struct PairValueRecord
521 friend struct PairPosFormat1;
524 GlyphID secondGlyph; /* GlyphID of second glyph in the
525 * pair--first glyph is listed in the
527 ValueRecord values; /* Positioning data for the first glyph
528 * followed by for second glyph */
530 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
534 friend struct PairPosFormat1;
536 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
537 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
539 if (!SANITIZE_SELF ()) return false;
540 unsigned int count = (1 + format_len) * len;
541 return SANITIZE_ARRAY (array, USHORT::get_size (), count);
545 USHORT len; /* Number of PairValueRecords */
547 array[VAR]; /* Array of PairValueRecords--ordered
548 * by GlyphID of the second glyph */
550 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
552 struct PairPosFormat1
554 friend struct PairPos;
557 inline bool apply (APPLY_ARG_DEF) const
560 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
561 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
564 unsigned int index = (this+coverage) (IN_CURGLYPH ());
565 if (HB_LIKELY (index == NOT_COVERED))
568 unsigned int j = buffer->in_pos + 1;
569 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
571 if (HB_UNLIKELY (j == end))
576 unsigned int len1 = valueFormat1.get_len ();
577 unsigned int len2 = valueFormat2.get_len ();
578 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
580 const PairSet &pair_set = this+pairSet[index];
581 unsigned int count = pair_set.len;
582 const PairValueRecord *record = pair_set.array;
583 for (unsigned int i = 0; i < count; i++)
585 if (IN_GLYPH (j) == record->secondGlyph)
587 valueFormat1.apply_value (context, CharP(this), &record->values[0], CURPOSITION ());
588 valueFormat2.apply_value (context, CharP(this), &record->values[len1], POSITION (j));
594 record = &StructAtOffset<PairValueRecord> (*record, record_size);
600 inline bool sanitize (SANITIZE_ARG_DEF) {
603 unsigned int len1 = valueFormat1.get_len ();
604 unsigned int len2 = valueFormat2.get_len ();
606 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
607 HB_LIKELY (pairSet.sanitize (SANITIZE_ARG, CharP(this), len1 + len2)))) return false;
609 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
611 unsigned int stride = 1 + len1 + len2;
612 unsigned int count1 = pairSet.len;
613 for (unsigned int i = 0; i < count1; i++)
615 const PairSet &pair_set = this+pairSet[i];
617 unsigned int count2 = pair_set.len;
618 const PairValueRecord *record = pair_set.array;
619 if (!(valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[0], count2, stride) &&
620 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[len1], count2, stride)))
628 USHORT format; /* Format identifier--format = 1 */
630 coverage; /* Offset to Coverage table--from
631 * beginning of subtable */
632 ValueFormat valueFormat1; /* Defines the types of data in
633 * ValueRecord1--for the first glyph
634 * in the pair--may be zero (0) */
635 ValueFormat valueFormat2; /* Defines the types of data in
636 * ValueRecord2--for the second glyph
637 * in the pair--may be zero (0) */
638 OffsetArrayOf<PairSet>
639 pairSet; /* Array of PairSet tables
640 * ordered by Coverage Index */
642 ASSERT_SIZE (PairPosFormat1, 10);
644 struct PairPosFormat2
646 friend struct PairPos;
649 inline bool apply (APPLY_ARG_DEF) const
652 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
653 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
656 unsigned int index = (this+coverage) (IN_CURGLYPH ());
657 if (HB_LIKELY (index == NOT_COVERED))
660 unsigned int j = buffer->in_pos + 1;
661 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
663 if (HB_UNLIKELY (j == end))
668 unsigned int len1 = valueFormat1.get_len ();
669 unsigned int len2 = valueFormat2.get_len ();
670 unsigned int record_len = len1 + len2;
672 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
673 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
674 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
677 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
678 valueFormat1.apply_value (context, CharP(this), v, CURPOSITION ());
679 valueFormat2.apply_value (context, CharP(this), v + len1, POSITION (j));
688 inline bool sanitize (SANITIZE_ARG_DEF) {
690 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
691 SANITIZE_THIS2 (classDef1, classDef2))) return false;
693 unsigned int len1 = valueFormat1.get_len ();
694 unsigned int len2 = valueFormat2.get_len ();
695 unsigned int stride = len1 + len2;
696 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
697 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
698 return SANITIZE_ARRAY (values, record_size, count) &&
699 valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[0], count, stride) &&
700 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[len1], count, stride);
704 USHORT format; /* Format identifier--format = 2 */
706 coverage; /* Offset to Coverage table--from
707 * beginning of subtable */
708 ValueFormat valueFormat1; /* ValueRecord definition--for the
709 * first glyph of the pair--may be zero
711 ValueFormat valueFormat2; /* ValueRecord definition--for the
712 * second glyph of the pair--may be
715 classDef1; /* Offset to ClassDef table--from
716 * beginning of PairPos subtable--for
717 * the first glyph of the pair */
719 classDef2; /* Offset to ClassDef table--from
720 * beginning of PairPos subtable--for
721 * the second glyph of the pair */
722 USHORT class1Count; /* Number of classes in ClassDef1
723 * table--includes Class0 */
724 USHORT class2Count; /* Number of classes in ClassDef2
725 * table--includes Class0 */
726 ValueRecord values; /* Matrix of value pairs:
727 * class1-major, class2-minor,
728 * Each entry has value1 and value2 */
730 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
734 friend struct PosLookupSubTable;
737 inline bool apply (APPLY_ARG_DEF) const
741 case 1: return u.format1->apply (APPLY_ARG);
742 case 2: return u.format2->apply (APPLY_ARG);
743 default:return false;
747 inline bool sanitize (SANITIZE_ARG_DEF) {
749 if (!SANITIZE (u.format)) return false;
751 case 1: return u.format1->sanitize (SANITIZE_ARG);
752 case 2: return u.format2->sanitize (SANITIZE_ARG);
759 USHORT format; /* Format identifier */
760 PairPosFormat1 format1[VAR];
761 PairPosFormat2 format2[VAR];
766 struct EntryExitRecord
768 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
770 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
772 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
776 entryAnchor; /* Offset to EntryAnchor table--from
777 * beginning of CursivePos
778 * subtable--may be NULL */
780 exitAnchor; /* Offset to ExitAnchor table--from
781 * beginning of CursivePos
782 * subtable--may be NULL */
784 ASSERT_SIZE (EntryExitRecord, 4);
786 struct CursivePosFormat1
788 friend struct CursivePos;
791 inline bool apply (APPLY_ARG_DEF) const
794 /* Now comes the messiest part of the whole OpenType
795 specification. At first glance, cursive connections seem easy
796 to understand, but there are pitfalls! The reason is that
797 the specs don't mention how to compute the advance values
798 resp. glyph offsets. I was told it would be an omission, to
799 be fixed in the next OpenType version... Again many thanks to
800 Andrei Burago <andreib@microsoft.com> for clarifications.
802 Consider the following example:
815 glyph1: advance width = 12
818 glyph2: advance width = 11
821 LSB is 1 for both glyphs (so the boxes drawn above are glyph
822 bboxes). Writing direction is R2L; `0' denotes the glyph's
825 Now the surprising part: The advance width of the *left* glyph
826 (resp. of the *bottom* glyph) will be modified, no matter
827 whether the writing direction is L2R or R2L (resp. T2B or
828 B2T)! This assymetry is caused by the fact that the glyph's
829 coordinate origin is always the lower left corner for all
832 Continuing the above example, we can compute the new
833 (horizontal) advance width of glyph2 as
837 and the new vertical offset of glyph2 as
842 Vertical writing direction is far more complicated:
844 a) Assuming that we recompute the advance height of the lower glyph:
851 yadv2 | 0+--+------+ -- BSB1 --
854 BSB2 -- 0+--------+ --
857 glyph1: advance height = 6
860 glyph2: advance height = 7
863 TSB is 1 for both glyphs; writing direction is T2B.
866 BSB1 = yadv1 - (TSB1 + ymax1)
867 BSB2 = yadv2 - (TSB2 + ymax2)
870 vertical advance width of glyph2
871 = y_offset + BSB2 - BSB1
872 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
873 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
874 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
877 b) Assuming that we recompute the advance height of the upper glyph:
882 TSB2 -- +-----+--+ 1 | yadv1 ymax1
884 yadv2 | 0+--+------+ -- --
885 ymax2 | 2 | -- y_offset
890 glyph1: advance height = 6
893 glyph2: advance height = 7
896 TSB is 1 for both glyphs; writing direction is T2B.
900 vertical advance width of glyph2
901 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
902 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
905 Comparing a) with b) shows that b) is easier to compute. I'll wait
906 for a reply from Andrei to see what should really be implemented...
908 Since horizontal advance widths or vertical advance heights
909 can be used alone but not together, no ambiguity occurs. */
911 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->info.gpos;
912 hb_codepoint_t last_pos = gpi->last;
913 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
915 /* We don't handle mark glyphs here. */
916 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
919 unsigned int index = (this+coverage) (IN_CURGLYPH ());
920 if (HB_LIKELY (index == NOT_COVERED))
923 const EntryExitRecord &record = entryExitRecord[index];
925 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
928 hb_position_t entry_x, entry_y;
929 (this+record.entryAnchor).get_anchor (context, IN_CURGLYPH (), &entry_x, &entry_y);
933 if (buffer->direction == HB_DIRECTION_RTL)
935 /* advance is absolute, not relative */
936 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
940 /* advance is absolute, not relative */
941 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
944 if (lookup_flag & LookupFlag::RightToLeft)
946 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
947 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
951 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
952 POSITION (buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
956 if (record.exitAnchor)
958 gpi->last = buffer->in_pos;
959 (this+record.exitAnchor).get_anchor (context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
966 inline bool sanitize (SANITIZE_ARG_DEF) {
968 return SANITIZE_THIS2 (coverage, entryExitRecord);
972 USHORT format; /* Format identifier--format = 1 */
974 coverage; /* Offset to Coverage table--from
975 * beginning of subtable */
976 ArrayOf<EntryExitRecord>
977 entryExitRecord; /* Array of EntryExit records--in
978 * Coverage Index order */
980 ASSERT_SIZE (CursivePosFormat1, 6);
984 friend struct PosLookupSubTable;
987 inline bool apply (APPLY_ARG_DEF) const
991 case 1: return u.format1->apply (APPLY_ARG);
992 default:return false;
996 inline bool sanitize (SANITIZE_ARG_DEF) {
998 if (!SANITIZE (u.format)) return false;
1000 case 1: return u.format1->sanitize (SANITIZE_ARG);
1001 default:return true;
1007 USHORT format; /* Format identifier */
1008 CursivePosFormat1 format1[VAR];
1013 typedef AnchorMatrix BaseArray; /* base-major--
1014 * in order of BaseCoverage Index--,
1016 * ordered by class--zero-based. */
1018 struct MarkBasePosFormat1
1020 friend struct MarkBasePos;
1023 inline bool apply (APPLY_ARG_DEF) const
1026 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1027 if (HB_LIKELY (mark_index == NOT_COVERED))
1030 /* now we search backwards for a non-mark glyph */
1031 unsigned int j = buffer->in_pos;
1034 if (HB_UNLIKELY (!j))
1037 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1040 /* The following assertion is too strong. */
1041 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1045 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1046 if (base_index == NOT_COVERED)
1049 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
1052 inline bool sanitize (SANITIZE_ARG_DEF) {
1054 return SANITIZE_SELF () && SANITIZE_THIS3 (markCoverage, baseCoverage, markArray) &&
1055 HB_LIKELY (baseArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1059 USHORT format; /* Format identifier--format = 1 */
1061 markCoverage; /* Offset to MarkCoverage table--from
1062 * beginning of MarkBasePos subtable */
1064 baseCoverage; /* Offset to BaseCoverage table--from
1065 * beginning of MarkBasePos subtable */
1066 USHORT classCount; /* Number of classes defined for marks */
1068 markArray; /* Offset to MarkArray table--from
1069 * beginning of MarkBasePos subtable */
1071 baseArray; /* Offset to BaseArray table--from
1072 * beginning of MarkBasePos subtable */
1074 ASSERT_SIZE (MarkBasePosFormat1, 12);
1078 friend struct PosLookupSubTable;
1081 inline bool apply (APPLY_ARG_DEF) const
1085 case 1: return u.format1->apply (APPLY_ARG);
1086 default:return false;
1090 inline bool sanitize (SANITIZE_ARG_DEF) {
1092 if (!SANITIZE (u.format)) return false;
1094 case 1: return u.format1->sanitize (SANITIZE_ARG);
1095 default:return true;
1101 USHORT format; /* Format identifier */
1102 MarkBasePosFormat1 format1[VAR];
1107 typedef AnchorMatrix LigatureAttach; /* component-major--
1108 * in order of writing direction--,
1110 * ordered by class--zero-based. */
1112 typedef OffsetListOf<LigatureAttach> LigatureArray;
1113 /* Array of LigatureAttach
1115 * LigatureCoverage Index */
1117 struct MarkLigPosFormat1
1119 friend struct MarkLigPos;
1122 inline bool apply (APPLY_ARG_DEF) const
1125 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1126 if (HB_LIKELY (mark_index == NOT_COVERED))
1129 /* now we search backwards for a non-mark glyph */
1130 unsigned int j = buffer->in_pos;
1133 if (HB_UNLIKELY (!j))
1136 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1139 /* The following assertion is too strong. */
1140 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1144 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1145 if (lig_index == NOT_COVERED)
1148 const LigatureArray& lig_array = this+ligatureArray;
1149 const LigatureAttach& lig_attach = lig_array[lig_index];
1151 /* Find component to attach to */
1152 unsigned int comp_count = lig_attach.rows;
1153 if (HB_UNLIKELY (!comp_count))
1155 unsigned int comp_index;
1156 /* We must now check whether the ligature ID of the current mark glyph
1157 * is identical to the ligature ID of the found ligature. If yes, we
1158 * can directly use the component index. If not, we attach the mark
1159 * glyph to the last component of the ligature. */
1160 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (buffer->in_pos) && IN_COMPONENT (buffer->in_pos))
1162 comp_index = IN_COMPONENT (buffer->in_pos) - 1;
1163 if (comp_index >= comp_count)
1164 comp_index = comp_count - 1;
1167 comp_index = comp_count - 1;
1169 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1172 inline bool sanitize (SANITIZE_ARG_DEF) {
1174 return SANITIZE_SELF () && SANITIZE_THIS3 (markCoverage, ligatureCoverage, markArray) &&
1175 HB_LIKELY (ligatureArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1179 USHORT format; /* Format identifier--format = 1 */
1181 markCoverage; /* Offset to Mark Coverage table--from
1182 * beginning of MarkLigPos subtable */
1184 ligatureCoverage; /* Offset to Ligature Coverage
1185 * table--from beginning of MarkLigPos
1187 USHORT classCount; /* Number of defined mark classes */
1189 markArray; /* Offset to MarkArray table--from
1190 * beginning of MarkLigPos subtable */
1191 OffsetTo<LigatureArray>
1192 ligatureArray; /* Offset to LigatureArray table--from
1193 * beginning of MarkLigPos subtable */
1195 ASSERT_SIZE (MarkLigPosFormat1, 12);
1199 friend struct PosLookupSubTable;
1202 inline bool apply (APPLY_ARG_DEF) const
1206 case 1: return u.format1->apply (APPLY_ARG);
1207 default:return false;
1211 inline bool sanitize (SANITIZE_ARG_DEF) {
1213 if (!SANITIZE (u.format)) return false;
1215 case 1: return u.format1->sanitize (SANITIZE_ARG);
1216 default:return true;
1222 USHORT format; /* Format identifier */
1223 MarkLigPosFormat1 format1[VAR];
1228 typedef AnchorMatrix Mark2Array; /* mark2-major--
1229 * in order of Mark2Coverage Index--,
1231 * ordered by class--zero-based. */
1233 struct MarkMarkPosFormat1
1235 friend struct MarkMarkPos;
1238 inline bool apply (APPLY_ARG_DEF) const
1241 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1242 if (HB_LIKELY (mark1_index == NOT_COVERED))
1245 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1246 unsigned int j = buffer->in_pos;
1249 if (HB_UNLIKELY (!j))
1252 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property));
1254 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1257 /* Two marks match only if they belong to the same base, or same component
1258 * of the same ligature. That is, the component numbers must match, and
1259 * if those are non-zero, the ligid number should also match. */
1260 if ((IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos)) ||
1261 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (buffer->in_pos)))
1264 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1265 if (mark2_index == NOT_COVERED)
1268 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1271 inline bool sanitize (SANITIZE_ARG_DEF) {
1273 return SANITIZE_SELF () && SANITIZE_THIS3 (mark1Coverage, mark2Coverage, mark1Array) &&
1274 HB_LIKELY (mark2Array.sanitize (SANITIZE_ARG, CharP(this), classCount));
1278 USHORT format; /* Format identifier--format = 1 */
1280 mark1Coverage; /* Offset to Combining Mark1 Coverage
1281 * table--from beginning of MarkMarkPos
1284 mark2Coverage; /* Offset to Combining Mark2 Coverage
1285 * table--from beginning of MarkMarkPos
1287 USHORT classCount; /* Number of defined mark classes */
1289 mark1Array; /* Offset to Mark1Array table--from
1290 * beginning of MarkMarkPos subtable */
1291 OffsetTo<Mark2Array>
1292 mark2Array; /* Offset to Mark2Array table--from
1293 * beginning of MarkMarkPos subtable */
1295 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1299 friend struct PosLookupSubTable;
1302 inline bool apply (APPLY_ARG_DEF) const
1306 case 1: return u.format1->apply (APPLY_ARG);
1307 default:return false;
1311 inline bool sanitize (SANITIZE_ARG_DEF) {
1313 if (!SANITIZE (u.format)) return false;
1315 case 1: return u.format1->sanitize (SANITIZE_ARG);
1316 default:return true;
1322 USHORT format; /* Format identifier */
1323 MarkMarkPosFormat1 format1[VAR];
1328 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1330 struct ContextPos : Context
1332 friend struct PosLookupSubTable;
1335 inline bool apply (APPLY_ARG_DEF) const
1338 return Context::apply (APPLY_ARG, position_lookup);
1342 struct ChainContextPos : ChainContext
1344 friend struct PosLookupSubTable;
1347 inline bool apply (APPLY_ARG_DEF) const
1350 return ChainContext::apply (APPLY_ARG, position_lookup);
1355 struct ExtensionPos : Extension
1357 friend struct PosLookupSubTable;
1360 inline const struct PosLookupSubTable& get_subtable (void) const
1362 unsigned int offset = get_offset ();
1363 if (HB_UNLIKELY (!offset)) return Null(PosLookupSubTable);
1364 return StructAtOffset<PosLookupSubTable> (*this, offset);
1367 inline bool apply (APPLY_ARG_DEF) const;
1369 inline bool sanitize (SANITIZE_ARG_DEF);
1379 struct PosLookupSubTable
1381 friend struct PosLookup;
1395 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1398 switch (lookup_type) {
1399 case Single: return u.single->apply (APPLY_ARG);
1400 case Pair: return u.pair->apply (APPLY_ARG);
1401 case Cursive: return u.cursive->apply (APPLY_ARG);
1402 case MarkBase: return u.markBase->apply (APPLY_ARG);
1403 case MarkLig: return u.markLig->apply (APPLY_ARG);
1404 case MarkMark: return u.markMark->apply (APPLY_ARG);
1405 case Context: return u.context->apply (APPLY_ARG);
1406 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1407 case Extension: return u.extension->apply (APPLY_ARG);
1408 default:return false;
1412 inline bool sanitize (SANITIZE_ARG_DEF) {
1414 if (!SANITIZE (u.format)) return false;
1416 case Single: return u.single->sanitize (SANITIZE_ARG);
1417 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1418 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1419 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1420 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1421 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1422 case Context: return u.context->sanitize (SANITIZE_ARG);
1423 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1424 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1425 default:return true;
1432 SinglePos single[VAR];
1434 CursivePos cursive[VAR];
1435 MarkBasePos markBase[VAR];
1436 MarkLigPos markLig[VAR];
1437 MarkMarkPos markMark[VAR];
1438 ContextPos context[VAR];
1439 ChainContextPos chainContext[VAR];
1440 ExtensionPos extension[VAR];
1445 struct PosLookup : Lookup
1447 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1448 { return this+Cast<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1450 /* Like get_type(), but looks through extension lookups.
1451 * Never returns Extension */
1452 inline unsigned int get_effective_type (void) const
1454 unsigned int type = get_type ();
1456 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
1458 unsigned int count = get_subtable_count ();
1459 type = get_subtable(0).u.extension->get_type ();
1460 /* The spec says all subtables should have the same type.
1461 * This is specially important if one has a reverse type! */
1462 for (unsigned int i = 1; i < count; i++)
1463 if (get_subtable(i).u.extension->get_type () != type)
1470 inline bool apply_once (hb_ot_layout_context_t *context,
1471 hb_buffer_t *buffer,
1472 unsigned int context_length,
1473 unsigned int nesting_level_left) const
1475 unsigned int lookup_type = get_type ();
1476 unsigned int lookup_flag = get_flag ();
1477 unsigned int property;
1479 if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
1482 for (unsigned int i = 0; i < get_subtable_count (); i++)
1483 if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
1489 inline bool apply_string (hb_ot_layout_context_t *context,
1490 hb_buffer_t *buffer,
1491 hb_mask_t mask) const
1495 if (HB_UNLIKELY (!buffer->in_length))
1498 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1501 while (buffer->in_pos < buffer->in_length)
1504 if (~IN_MASK (buffer->in_pos) & mask)
1506 done = apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1512 /* Contrary to properties defined in GDEF, user-defined properties
1513 will always stop a possible cursive positioning. */
1514 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1524 inline bool sanitize (SANITIZE_ARG_DEF) {
1526 if (HB_UNLIKELY (!Lookup::sanitize (SANITIZE_ARG))) return false;
1527 OffsetArrayOf<PosLookupSubTable> &list = Cast<OffsetArrayOf<PosLookupSubTable> > (subTable);
1528 return SANITIZE_THIS (list);
1532 typedef OffsetListOf<PosLookup> PosLookupList;
1533 ASSERT_SIZE (PosLookupList, 2);
1539 struct GPOS : GSUBGPOS
1541 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1543 static inline const GPOS& get_for_data (const char *data)
1544 { return Cast<GPOS> (GSUBGPOS::get_for_data (data)); }
1546 inline const PosLookup& get_lookup (unsigned int i) const
1547 { return Cast<PosLookup> (GSUBGPOS::get_lookup (i)); }
1549 inline bool position_lookup (hb_ot_layout_context_t *context,
1550 hb_buffer_t *buffer,
1551 unsigned int lookup_index,
1552 hb_mask_t mask) const
1553 { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
1555 inline bool sanitize (SANITIZE_ARG_DEF) {
1557 if (HB_UNLIKELY (!GSUBGPOS::sanitize (SANITIZE_ARG))) return false;
1558 OffsetTo<PosLookupList> &list = Cast<OffsetTo<PosLookupList> > (lookupList);
1559 return SANITIZE_THIS (list);
1562 ASSERT_SIZE (GPOS, 10);
1565 /* Out-of-class implementation for methods recursing */
1567 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1570 unsigned int lookup_type = get_type ();
1572 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
1575 return get_subtable ().apply (APPLY_ARG, lookup_type);
1578 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1581 if (HB_UNLIKELY (!Extension::sanitize (SANITIZE_ARG))) return false;
1582 if (HB_UNLIKELY (get_type () == PosLookupSubTable::Extension)) return false;
1584 unsigned int offset = get_offset ();
1585 if (HB_UNLIKELY (!offset)) return true;
1586 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1589 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1591 const GPOS &gpos = *(context->face->ot_layout.gpos);
1592 const PosLookup &l = gpos.get_lookup (lookup_index);
1594 if (HB_UNLIKELY (nesting_level_left == 0))
1596 nesting_level_left--;
1598 if (HB_UNLIKELY (context_length < 1))
1601 return l.apply_once (context, buffer, context_length, nesting_level_left);
1605 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */