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 inline bool sanitize_values_stride_unsafe (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count, unsigned int stride) {
177 if (!has_device ()) return true;
179 for (unsigned int i = 0; i < count; i++) {
180 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
188 ASSERT_SIZE (ValueFormat, 2);
193 friend struct Anchor;
196 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id HB_GNUC_UNUSED,
197 hb_position_t *x, hb_position_t *y) const
199 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
200 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
203 inline bool sanitize (SANITIZE_ARG_DEF) {
205 return SANITIZE_SELF ();
209 USHORT format; /* Format identifier--format = 1 */
210 SHORT xCoordinate; /* Horizontal value--in design units */
211 SHORT yCoordinate; /* Vertical value--in design units */
213 ASSERT_SIZE (AnchorFormat1, 6);
217 friend struct Anchor;
220 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
221 hb_position_t *x, hb_position_t *y) const
224 * NOTE only adjust directions with nonzero ppem */
225 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
226 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
229 inline bool sanitize (SANITIZE_ARG_DEF) {
231 return SANITIZE_SELF ();
235 USHORT format; /* Format identifier--format = 2 */
236 SHORT xCoordinate; /* Horizontal value--in design units */
237 SHORT yCoordinate; /* Vertical value--in design units */
238 USHORT anchorPoint; /* Index to glyph contour point */
240 ASSERT_SIZE (AnchorFormat2, 8);
244 friend struct Anchor;
247 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id HB_GNUC_UNUSED,
248 hb_position_t *x, hb_position_t *y) const
250 *x = _hb_16dot16_mul_round (context->font->x_scale, xCoordinate);
251 *y = _hb_16dot16_mul_round (context->font->y_scale, yCoordinate);
253 /* pixel -> fractional pixel */
254 if (context->font->x_ppem)
255 *x += (this+xDeviceTable).get_delta (context->font->x_ppem) << 16;
256 if (context->font->y_ppem)
257 *y += (this+yDeviceTable).get_delta (context->font->y_ppem) << 16;
260 inline bool sanitize (SANITIZE_ARG_DEF) {
262 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
266 USHORT format; /* Format identifier--format = 3 */
267 SHORT xCoordinate; /* Horizontal value--in design units */
268 SHORT yCoordinate; /* Vertical value--in design units */
270 xDeviceTable; /* Offset to Device table for X
271 * coordinate-- from beginning of
272 * Anchor table (may be NULL) */
274 yDeviceTable; /* Offset to Device table for Y
275 * coordinate-- from beginning of
276 * Anchor table (may be NULL) */
278 ASSERT_SIZE (AnchorFormat3, 10);
282 inline void get_anchor (hb_ot_layout_context_t *context, hb_codepoint_t glyph_id,
283 hb_position_t *x, hb_position_t *y) const
287 case 1: u.format1->get_anchor (context, glyph_id, x, y); return;
288 case 2: u.format2->get_anchor (context, glyph_id, x, y); return;
289 case 3: u.format3->get_anchor (context, glyph_id, x, y); return;
294 inline bool sanitize (SANITIZE_ARG_DEF) {
296 if (!SANITIZE (u.format)) return false;
298 case 1: return u.format1->sanitize (SANITIZE_ARG);
299 case 2: return u.format2->sanitize (SANITIZE_ARG);
300 case 3: return u.format3->sanitize (SANITIZE_ARG);
307 USHORT format; /* Format identifier */
308 AnchorFormat1 format1[VAR];
309 AnchorFormat2 format2[VAR];
310 AnchorFormat3 format3[VAR];
317 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
318 if (HB_UNLIKELY (row >= rows || col >= cols)) return Null(Anchor);
319 return this+matrix[row * cols + col];
322 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
324 if (!SANITIZE_SELF ()) return false;
325 if (HB_UNLIKELY (cols >= ((unsigned int) -1) / rows)) return false;
326 unsigned int count = rows * cols;
327 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
328 for (unsigned int i = 0; i < count; i++)
329 if (!SANITIZE_THIS (matrix[i])) return false;
333 USHORT rows; /* Number of rows */
336 matrix[VAR]; /* Matrix of offsets to Anchor tables--
337 * from beginning of AnchorMatrix table */
339 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
344 friend struct MarkArray;
346 static inline unsigned int get_size () { return sizeof (MarkRecord); }
348 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
350 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
354 USHORT klass; /* Class defined for this mark */
356 markAnchor; /* Offset to Anchor table--from
357 * beginning of MarkArray table */
359 ASSERT_SIZE (MarkRecord, 4);
363 inline bool apply (APPLY_ARG_DEF,
364 unsigned int mark_index, unsigned int glyph_index,
365 const AnchorMatrix &anchors, unsigned int class_count,
366 unsigned int glyph_pos) const
369 const MarkRecord &record = markRecord[mark_index];
370 unsigned int mark_class = record.klass;
372 const Anchor& mark_anchor = this + record.markAnchor;
373 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
375 hb_position_t mark_x, mark_y, base_x, base_y;
377 mark_anchor.get_anchor (context, IN_CURGLYPH (), &mark_x, &mark_y);
378 glyph_anchor.get_anchor (context, IN_GLYPH (glyph_pos), &base_x, &base_y);
380 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
383 o->x_offset = base_x - mark_x;
384 o->y_offset = base_y - mark_y;
385 o->back = buffer->in_pos - glyph_pos;
391 inline bool sanitize (SANITIZE_ARG_DEF) {
393 return SANITIZE_THIS (markRecord);
398 markRecord; /* Array of MarkRecords--in Coverage order */
400 ASSERT_SIZE (MarkArray, 2);
405 struct SinglePosFormat1
407 friend struct SinglePos;
410 inline bool apply (APPLY_ARG_DEF) const
413 unsigned int index = (this+coverage) (IN_CURGLYPH ());
414 if (HB_LIKELY (index == NOT_COVERED))
417 valueFormat.apply_value (context, CharP(this), values, CURPOSITION ());
423 inline bool sanitize (SANITIZE_ARG_DEF) {
425 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
426 valueFormat.sanitize_value (SANITIZE_ARG, CharP(this), values);
430 USHORT format; /* Format identifier--format = 1 */
432 coverage; /* Offset to Coverage table--from
433 * beginning of subtable */
434 ValueFormat valueFormat; /* Defines the types of data in the
436 ValueRecord values; /* Defines positioning
437 * value(s)--applied to all glyphs in
438 * the Coverage table */
440 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
442 struct SinglePosFormat2
444 friend struct SinglePos;
447 inline bool apply (APPLY_ARG_DEF) const
450 unsigned int index = (this+coverage) (IN_CURGLYPH ());
451 if (HB_LIKELY (index == NOT_COVERED))
454 if (HB_LIKELY (index >= valueCount))
457 valueFormat.apply_value (context, CharP(this),
458 &values[index * valueFormat.get_len ()],
465 inline bool sanitize (SANITIZE_ARG_DEF) {
467 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
468 valueFormat.sanitize_values (SANITIZE_ARG, CharP(this), values, valueCount);
472 USHORT format; /* Format identifier--format = 2 */
474 coverage; /* Offset to Coverage table--from
475 * beginning of subtable */
476 ValueFormat valueFormat; /* Defines the types of data in the
478 USHORT valueCount; /* Number of ValueRecords */
479 ValueRecord values; /* Array of ValueRecords--positioning
480 * values applied to glyphs */
482 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
486 friend struct PosLookupSubTable;
489 inline bool apply (APPLY_ARG_DEF) const
493 case 1: return u.format1->apply (APPLY_ARG);
494 case 2: return u.format2->apply (APPLY_ARG);
495 default:return false;
499 inline bool sanitize (SANITIZE_ARG_DEF) {
501 if (!SANITIZE (u.format)) return false;
503 case 1: return u.format1->sanitize (SANITIZE_ARG);
504 case 2: return u.format2->sanitize (SANITIZE_ARG);
511 USHORT format; /* Format identifier */
512 SinglePosFormat1 format1[VAR];
513 SinglePosFormat2 format2[VAR];
518 struct PairValueRecord
520 friend struct PairPosFormat1;
523 GlyphID secondGlyph; /* GlyphID of second glyph in the
524 * pair--first glyph is listed in the
526 ValueRecord values; /* Positioning data for the first glyph
527 * followed by for second glyph */
529 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
533 friend struct PairPosFormat1;
535 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
536 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
538 if (!SANITIZE_SELF ()) return false;
539 unsigned int count = (1 + format_len) * len;
540 return SANITIZE_MEM (array, USHORT::get_size () * count);
544 USHORT len; /* Number of PairValueRecords */
546 array[VAR]; /* Array of PairValueRecords--ordered
547 * by GlyphID of the second glyph */
549 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
551 struct PairPosFormat1
553 friend struct PairPos;
556 inline bool apply (APPLY_ARG_DEF) const
559 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
560 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
563 unsigned int index = (this+coverage) (IN_CURGLYPH ());
564 if (HB_LIKELY (index == NOT_COVERED))
567 unsigned int j = buffer->in_pos + 1;
568 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
570 if (HB_UNLIKELY (j == end))
575 unsigned int len1 = valueFormat1.get_len ();
576 unsigned int len2 = valueFormat2.get_len ();
577 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
579 const PairSet &pair_set = this+pairSet[index];
580 unsigned int count = pair_set.len;
581 const PairValueRecord *record = pair_set.array;
582 for (unsigned int i = 0; i < count; i++)
584 if (IN_GLYPH (j) == record->secondGlyph)
586 valueFormat1.apply_value (context, CharP(this), &record->values[0], CURPOSITION ());
587 valueFormat2.apply_value (context, CharP(this), &record->values[len1], POSITION (j));
593 record = &CONST_CAST (PairValueRecord, *record, record_size);
599 inline bool sanitize (SANITIZE_ARG_DEF) {
602 unsigned int len1 = valueFormat1.get_len ();
603 unsigned int len2 = valueFormat2.get_len ();
605 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
606 pairSet.sanitize (SANITIZE_ARG, CharP(this), len1 + len2))) return false;
608 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
610 unsigned int stride = 1 + len1 + len2;
611 unsigned int count1 = pairSet.len;
612 for (unsigned int i = 0; i < count1; i++)
614 const PairSet &pair_set = this+pairSet[i];
616 unsigned int count2 = pair_set.len;
617 const PairValueRecord *record = pair_set.array;
618 if (!(valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[0], count2, stride) &&
619 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[len1], count2, stride)))
627 USHORT format; /* Format identifier--format = 1 */
629 coverage; /* Offset to Coverage table--from
630 * beginning of subtable */
631 ValueFormat valueFormat1; /* Defines the types of data in
632 * ValueRecord1--for the first glyph
633 * in the pair--may be zero (0) */
634 ValueFormat valueFormat2; /* Defines the types of data in
635 * ValueRecord2--for the second glyph
636 * in the pair--may be zero (0) */
637 OffsetArrayOf<PairSet>
638 pairSet; /* Array of PairSet tables
639 * ordered by Coverage Index */
641 ASSERT_SIZE (PairPosFormat1, 10);
643 struct PairPosFormat2
645 friend struct PairPos;
648 inline bool apply (APPLY_ARG_DEF) const
651 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
652 if (HB_UNLIKELY (buffer->in_pos + 2 > end))
655 unsigned int index = (this+coverage) (IN_CURGLYPH ());
656 if (HB_LIKELY (index == NOT_COVERED))
659 unsigned int j = buffer->in_pos + 1;
660 while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, NULL))
662 if (HB_UNLIKELY (j == end))
667 unsigned int len1 = valueFormat1.get_len ();
668 unsigned int len2 = valueFormat2.get_len ();
669 unsigned int record_len = len1 + len2;
671 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
672 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
673 if (HB_UNLIKELY (klass1 >= class1Count || klass2 >= class2Count))
676 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
677 valueFormat1.apply_value (context, CharP(this), v, CURPOSITION ());
678 valueFormat2.apply_value (context, CharP(this), v + len1, POSITION (j));
687 inline bool sanitize (SANITIZE_ARG_DEF) {
689 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
690 SANITIZE_THIS2 (classDef1, classDef2))) return false;
692 unsigned int len1 = valueFormat1.get_len ();
693 unsigned int len2 = valueFormat2.get_len ();
694 unsigned int stride = len1 + len2;
695 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
696 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
697 return SANITIZE_ARRAY (values, record_size, count) &&
698 valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[0], count, stride) &&
699 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[len1], count, stride);
703 USHORT format; /* Format identifier--format = 2 */
705 coverage; /* Offset to Coverage table--from
706 * beginning of subtable */
707 ValueFormat valueFormat1; /* ValueRecord definition--for the
708 * first glyph of the pair--may be zero
710 ValueFormat valueFormat2; /* ValueRecord definition--for the
711 * second glyph of the pair--may be
714 classDef1; /* Offset to ClassDef table--from
715 * beginning of PairPos subtable--for
716 * the first glyph of the pair */
718 classDef2; /* Offset to ClassDef table--from
719 * beginning of PairPos subtable--for
720 * the second glyph of the pair */
721 USHORT class1Count; /* Number of classes in ClassDef1
722 * table--includes Class0 */
723 USHORT class2Count; /* Number of classes in ClassDef2
724 * table--includes Class0 */
725 ValueRecord values; /* Matrix of value pairs:
726 * class1-major, class2-minor,
727 * Each entry has value1 and value2 */
729 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
733 friend struct PosLookupSubTable;
736 inline bool apply (APPLY_ARG_DEF) const
740 case 1: return u.format1->apply (APPLY_ARG);
741 case 2: return u.format2->apply (APPLY_ARG);
742 default:return false;
746 inline bool sanitize (SANITIZE_ARG_DEF) {
748 if (!SANITIZE (u.format)) return false;
750 case 1: return u.format1->sanitize (SANITIZE_ARG);
751 case 2: return u.format2->sanitize (SANITIZE_ARG);
758 USHORT format; /* Format identifier */
759 PairPosFormat1 format1[VAR];
760 PairPosFormat2 format2[VAR];
765 struct EntryExitRecord
767 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
769 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
771 return SANITIZE_BASE2 (entryAnchor, exitAnchor, base);
775 entryAnchor; /* Offset to EntryAnchor table--from
776 * beginning of CursivePos
777 * subtable--may be NULL */
779 exitAnchor; /* Offset to ExitAnchor table--from
780 * beginning of CursivePos
781 * subtable--may be NULL */
783 ASSERT_SIZE (EntryExitRecord, 4);
785 struct CursivePosFormat1
787 friend struct CursivePos;
790 inline bool apply (APPLY_ARG_DEF) const
793 /* Now comes the messiest part of the whole OpenType
794 specification. At first glance, cursive connections seem easy
795 to understand, but there are pitfalls! The reason is that
796 the specs don't mention how to compute the advance values
797 resp. glyph offsets. I was told it would be an omission, to
798 be fixed in the next OpenType version... Again many thanks to
799 Andrei Burago <andreib@microsoft.com> for clarifications.
801 Consider the following example:
814 glyph1: advance width = 12
817 glyph2: advance width = 11
820 LSB is 1 for both glyphs (so the boxes drawn above are glyph
821 bboxes). Writing direction is R2L; `0' denotes the glyph's
824 Now the surprising part: The advance width of the *left* glyph
825 (resp. of the *bottom* glyph) will be modified, no matter
826 whether the writing direction is L2R or R2L (resp. T2B or
827 B2T)! This assymetry is caused by the fact that the glyph's
828 coordinate origin is always the lower left corner for all
831 Continuing the above example, we can compute the new
832 (horizontal) advance width of glyph2 as
836 and the new vertical offset of glyph2 as
841 Vertical writing direction is far more complicated:
843 a) Assuming that we recompute the advance height of the lower glyph:
850 yadv2 | 0+--+------+ -- BSB1 --
853 BSB2 -- 0+--------+ --
856 glyph1: advance height = 6
859 glyph2: advance height = 7
862 TSB is 1 for both glyphs; writing direction is T2B.
865 BSB1 = yadv1 - (TSB1 + ymax1)
866 BSB2 = yadv2 - (TSB2 + ymax2)
869 vertical advance width of glyph2
870 = y_offset + BSB2 - BSB1
871 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
872 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
873 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
876 b) Assuming that we recompute the advance height of the upper glyph:
881 TSB2 -- +-----+--+ 1 | yadv1 ymax1
883 yadv2 | 0+--+------+ -- --
884 ymax2 | 2 | -- y_offset
889 glyph1: advance height = 6
892 glyph2: advance height = 7
895 TSB is 1 for both glyphs; writing direction is T2B.
899 vertical advance width of glyph2
900 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
901 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
904 Comparing a) with b) shows that b) is easier to compute. I'll wait
905 for a reply from Andrei to see what should really be implemented...
907 Since horizontal advance widths or vertical advance heights
908 can be used alone but not together, no ambiguity occurs. */
910 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &context->info.gpos;
911 hb_codepoint_t last_pos = gpi->last;
912 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
914 /* We don't handle mark glyphs here. */
915 if (property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
918 unsigned int index = (this+coverage) (IN_CURGLYPH ());
919 if (HB_LIKELY (index == NOT_COVERED))
922 const EntryExitRecord &record = entryExitRecord[index];
924 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
927 hb_position_t entry_x, entry_y;
928 (this+record.entryAnchor).get_anchor (context, IN_CURGLYPH (), &entry_x, &entry_y);
932 if (buffer->direction == HB_DIRECTION_RTL)
934 /* advance is absolute, not relative */
935 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
939 /* advance is absolute, not relative */
940 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
943 if (lookup_flag & LookupFlag::RightToLeft)
945 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
946 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
950 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
951 POSITION (buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
955 if (record.exitAnchor)
957 gpi->last = buffer->in_pos;
958 (this+record.exitAnchor).get_anchor (context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
965 inline bool sanitize (SANITIZE_ARG_DEF) {
967 return SANITIZE_THIS2 (coverage, entryExitRecord);
971 USHORT format; /* Format identifier--format = 1 */
973 coverage; /* Offset to Coverage table--from
974 * beginning of subtable */
975 ArrayOf<EntryExitRecord>
976 entryExitRecord; /* Array of EntryExit records--in
977 * Coverage Index order */
979 ASSERT_SIZE (CursivePosFormat1, 6);
983 friend struct PosLookupSubTable;
986 inline bool apply (APPLY_ARG_DEF) const
990 case 1: return u.format1->apply (APPLY_ARG);
991 default:return false;
995 inline bool sanitize (SANITIZE_ARG_DEF) {
997 if (!SANITIZE (u.format)) return false;
999 case 1: return u.format1->sanitize (SANITIZE_ARG);
1000 default:return true;
1006 USHORT format; /* Format identifier */
1007 CursivePosFormat1 format1[VAR];
1012 typedef AnchorMatrix BaseArray; /* base-major--
1013 * in order of BaseCoverage Index--,
1015 * ordered by class--zero-based. */
1017 struct MarkBasePosFormat1
1019 friend struct MarkBasePos;
1022 inline bool apply (APPLY_ARG_DEF) const
1025 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1026 if (HB_LIKELY (mark_index == NOT_COVERED))
1029 /* now we search backwards for a non-mark glyph */
1030 unsigned int j = buffer->in_pos;
1033 if (HB_UNLIKELY (!j))
1036 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1039 /* The following assertion is too strong. */
1040 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1044 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1045 if (base_index == NOT_COVERED)
1048 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
1051 inline bool sanitize (SANITIZE_ARG_DEF) {
1053 return SANITIZE_SELF () && SANITIZE_THIS2 (markCoverage, baseCoverage) &&
1054 SANITIZE_THIS (markArray) && baseArray.sanitize (SANITIZE_ARG, CharP(this), classCount);
1058 USHORT format; /* Format identifier--format = 1 */
1060 markCoverage; /* Offset to MarkCoverage table--from
1061 * beginning of MarkBasePos subtable */
1063 baseCoverage; /* Offset to BaseCoverage table--from
1064 * beginning of MarkBasePos subtable */
1065 USHORT classCount; /* Number of classes defined for marks */
1067 markArray; /* Offset to MarkArray table--from
1068 * beginning of MarkBasePos subtable */
1070 baseArray; /* Offset to BaseArray table--from
1071 * beginning of MarkBasePos subtable */
1073 ASSERT_SIZE (MarkBasePosFormat1, 12);
1077 friend struct PosLookupSubTable;
1080 inline bool apply (APPLY_ARG_DEF) const
1084 case 1: return u.format1->apply (APPLY_ARG);
1085 default:return false;
1089 inline bool sanitize (SANITIZE_ARG_DEF) {
1091 if (!SANITIZE (u.format)) return false;
1093 case 1: return u.format1->sanitize (SANITIZE_ARG);
1094 default:return true;
1100 USHORT format; /* Format identifier */
1101 MarkBasePosFormat1 format1[VAR];
1106 typedef AnchorMatrix LigatureAttach; /* component-major--
1107 * in order of writing direction--,
1109 * ordered by class--zero-based. */
1111 typedef OffsetListOf<LigatureAttach> LigatureArray;
1112 /* Array of LigatureAttach
1114 * LigatureCoverage Index */
1116 struct MarkLigPosFormat1
1118 friend struct MarkLigPos;
1121 inline bool apply (APPLY_ARG_DEF) const
1124 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1125 if (HB_LIKELY (mark_index == NOT_COVERED))
1128 /* now we search backwards for a non-mark glyph */
1129 unsigned int j = buffer->in_pos;
1132 if (HB_UNLIKELY (!j))
1135 } while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1138 /* The following assertion is too strong. */
1139 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1143 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1144 if (lig_index == NOT_COVERED)
1147 const LigatureArray& lig_array = this+ligatureArray;
1148 const LigatureAttach& lig_attach = lig_array[lig_index];
1150 /* Find component to attach to */
1151 unsigned int comp_count = lig_attach.rows;
1152 if (HB_UNLIKELY (!comp_count))
1154 unsigned int comp_index;
1155 /* We must now check whether the ligature ID of the current mark glyph
1156 * is identical to the ligature ID of the found ligature. If yes, we
1157 * can directly use the component index. If not, we attach the mark
1158 * glyph to the last component of the ligature. */
1159 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (buffer->in_pos) && IN_COMPONENT (buffer->in_pos))
1161 comp_index = IN_COMPONENT (buffer->in_pos) - 1;
1162 if (comp_index >= comp_count)
1163 comp_index = comp_count - 1;
1166 comp_index = comp_count - 1;
1168 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1171 inline bool sanitize (SANITIZE_ARG_DEF) {
1173 return SANITIZE_SELF () &&
1174 SANITIZE_THIS2 (markCoverage, ligatureCoverage) &&
1175 SANITIZE_THIS (markArray) && 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_THIS2 (mark1Coverage, mark2Coverage) &&
1274 SANITIZE_THIS (mark1Array) && 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
1361 { return CONST_CAST (PosLookupSubTable, Extension::get_subtable (), 0); }
1363 inline bool apply (APPLY_ARG_DEF) const;
1365 inline bool sanitize (SANITIZE_ARG_DEF);
1375 struct PosLookupSubTable
1377 friend struct PosLookup;
1391 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1394 switch (lookup_type) {
1395 case Single: return u.single->apply (APPLY_ARG);
1396 case Pair: return u.pair->apply (APPLY_ARG);
1397 case Cursive: return u.cursive->apply (APPLY_ARG);
1398 case MarkBase: return u.markBase->apply (APPLY_ARG);
1399 case MarkLig: return u.markLig->apply (APPLY_ARG);
1400 case MarkMark: return u.markMark->apply (APPLY_ARG);
1401 case Context: return u.context->apply (APPLY_ARG);
1402 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1403 case Extension: return u.extension->apply (APPLY_ARG);
1404 default:return false;
1408 inline bool sanitize (SANITIZE_ARG_DEF) {
1410 if (!SANITIZE (u.format)) return false;
1412 case Single: return u.single->sanitize (SANITIZE_ARG);
1413 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1414 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1415 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1416 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1417 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1418 case Context: return u.context->sanitize (SANITIZE_ARG);
1419 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1420 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1421 default:return true;
1428 SinglePos single[VAR];
1430 CursivePos cursive[VAR];
1431 MarkBasePos markBase[VAR];
1432 MarkLigPos markLig[VAR];
1433 MarkMarkPos markMark[VAR];
1434 ContextPos context[VAR];
1435 ChainContextPos chainContext[VAR];
1436 ExtensionPos extension[VAR];
1441 struct PosLookup : Lookup
1443 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1444 { return this+CONST_CAST (OffsetArrayOf<PosLookupSubTable>, subTable, 0)[i]; }
1446 /* Like get_type(), but looks through extension lookups.
1447 * Never returns Extension */
1448 inline unsigned int get_effective_type (void) const
1450 unsigned int type = get_type ();
1452 if (HB_UNLIKELY (type == PosLookupSubTable::Extension))
1454 unsigned int count = get_subtable_count ();
1455 type = get_subtable(0).u.extension->get_type ();
1456 /* The spec says all subtables should have the same type.
1457 * This is specially important if one has a reverse type! */
1458 for (unsigned int i = 1; i < count; i++)
1459 if (get_subtable(i).u.extension->get_type () != type)
1466 inline bool apply_once (hb_ot_layout_context_t *context,
1467 hb_buffer_t *buffer,
1468 unsigned int context_length,
1469 unsigned int nesting_level_left) const
1471 unsigned int lookup_type = get_type ();
1472 unsigned int lookup_flag = get_flag ();
1473 unsigned int property;
1475 if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
1478 for (unsigned int i = 0; i < get_subtable_count (); i++)
1479 if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
1485 inline bool apply_string (hb_ot_layout_context_t *context,
1486 hb_buffer_t *buffer,
1487 hb_mask_t mask) const
1491 if (HB_UNLIKELY (!buffer->in_length))
1494 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1497 while (buffer->in_pos < buffer->in_length)
1500 if (~IN_MASK (buffer->in_pos) & mask)
1502 done = apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1508 /* Contrary to properties defined in GDEF, user-defined properties
1509 will always stop a possible cursive positioning. */
1510 context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1520 inline bool sanitize (SANITIZE_ARG_DEF) {
1522 if (!Lookup::sanitize (SANITIZE_ARG)) return false;
1523 OffsetArrayOf<PosLookupSubTable> &list = CAST (OffsetArrayOf<PosLookupSubTable>, subTable, 0);
1524 return SANITIZE_THIS (list);
1528 typedef OffsetListOf<PosLookup> PosLookupList;
1529 ASSERT_SIZE (PosLookupList, 2);
1535 struct GPOS : GSUBGPOS
1537 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1539 static inline const GPOS& get_for_data (const char *data)
1540 { return CONST_CAST(GPOS, GSUBGPOS::get_for_data (data), 0); }
1542 inline const PosLookup& get_lookup (unsigned int i) const
1543 { return CONST_CAST(PosLookup, GSUBGPOS::get_lookup (i), 0); }
1545 inline bool position_lookup (hb_ot_layout_context_t *context,
1546 hb_buffer_t *buffer,
1547 unsigned int lookup_index,
1548 hb_mask_t mask) const
1549 { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
1551 inline bool sanitize (SANITIZE_ARG_DEF) {
1553 if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
1554 OffsetTo<PosLookupList> &list = CAST(OffsetTo<PosLookupList>, lookupList, 0);
1555 return SANITIZE_THIS (list);
1558 ASSERT_SIZE (GPOS, 10);
1561 /* Out-of-class implementation for methods recursing */
1563 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1566 unsigned int lookup_type = get_type ();
1568 if (HB_UNLIKELY (lookup_type == PosLookupSubTable::Extension))
1571 return get_subtable ().apply (APPLY_ARG, lookup_type);
1574 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1577 return Extension::sanitize (SANITIZE_ARG) &&
1578 (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
1579 get_type () == PosLookupSubTable::Extension ||
1580 DECONST_CAST (PosLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
1583 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1585 const GPOS &gpos = *(context->face->ot_layout.gpos);
1586 const PosLookup &l = gpos.get_lookup (lookup_index);
1588 if (HB_UNLIKELY (nesting_level_left == 0))
1590 nesting_level_left--;
1592 if (HB_UNLIKELY (context_length < 1))
1595 return l.apply_once (context, buffer, context_length, nesting_level_left);
1599 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */