2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
30 #define HB_OT_LAYOUT_GPOS_TABLE_HH
32 #include "hb-ot-layout-gsubgpos-private.hh"
36 /* buffer **position** var allocations */
37 #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
38 #define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */
41 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
45 typedef Value ValueRecord[VAR];
47 struct ValueFormat : USHORT
50 xPlacement = 0x0001, /* Includes horizontal adjustment for placement */
51 yPlacement = 0x0002, /* Includes vertical adjustment for placement */
52 xAdvance = 0x0004, /* Includes horizontal adjustment for advance */
53 yAdvance = 0x0008, /* Includes vertical adjustment for advance */
54 xPlaDevice = 0x0010, /* Includes horizontal Device table for placement */
55 yPlaDevice = 0x0020, /* Includes vertical Device table for placement */
56 xAdvDevice = 0x0040, /* Includes horizontal Device table for advance */
57 yAdvDevice = 0x0080, /* Includes vertical Device table for advance */
58 ignored = 0x0F00, /* Was used in TrueType Open for MM fonts */
59 reserved = 0xF000, /* For future use */
61 devices = 0x00F0 /* Mask for having any Device table */
64 /* All fields are options. Only those available advance the value pointer. */
66 SHORT xPlacement; /* Horizontal adjustment for
67 * placement--in design units */
68 SHORT yPlacement; /* Vertical adjustment for
69 * placement--in design units */
70 SHORT xAdvance; /* Horizontal adjustment for
71 * advance--in design units (only used
72 * for horizontal writing) */
73 SHORT yAdvance; /* Vertical adjustment for advance--in
74 * design units (only used for vertical
76 Offset xPlaDevice; /* Offset to Device table for
77 * horizontal placement--measured from
78 * beginning of PosTable (may be NULL) */
79 Offset yPlaDevice; /* Offset to Device table for vertical
80 * placement--measured from beginning
81 * of PosTable (may be NULL) */
82 Offset xAdvDevice; /* Offset to Device table for
83 * horizontal advance--measured from
84 * beginning of PosTable (may be NULL) */
85 Offset yAdvDevice; /* Offset to Device table for vertical
86 * advance--measured from beginning of
87 * PosTable (may be NULL) */
90 inline unsigned int get_len (void) const
91 { return _hb_popcount32 ((unsigned int) *this); }
92 inline unsigned int get_size (void) const
93 { return get_len () * Value::static_size; }
95 void apply_value (hb_font_t *font,
96 hb_direction_t direction,
99 hb_glyph_position_t &glyph_pos) const
101 unsigned int x_ppem, y_ppem;
102 unsigned int format = *this;
103 hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (direction);
107 if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++));
108 if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++));
109 if (format & xAdvance) {
110 if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values++)); else values++;
112 /* y_advance values grow downward but font-space grows upward, hence negation */
113 if (format & yAdvance) {
114 if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values++)); else values++;
117 if (!has_device ()) return;
119 x_ppem = font->x_ppem;
120 y_ppem = font->y_ppem;
122 if (!x_ppem && !y_ppem) return;
124 /* pixel -> fractional pixel */
125 if (format & xPlaDevice) {
126 if (x_ppem) glyph_pos.x_offset += (base + get_device (values++)).get_x_delta (font); else values++;
128 if (format & yPlaDevice) {
129 if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_y_delta (font); else values++;
131 if (format & xAdvDevice) {
132 if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (font); else values++;
134 if (format & yAdvDevice) {
135 /* y_advance values grow downward but font-space grows upward, hence negation */
136 if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values++)).get_y_delta (font); else values++;
141 inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
142 unsigned int format = *this;
144 if (format & xPlacement) values++;
145 if (format & yPlacement) values++;
146 if (format & xAdvance) values++;
147 if (format & yAdvance) values++;
149 if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
150 if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
151 if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
152 if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
157 static inline OffsetTo<Device>& get_device (Value* value)
158 { return *CastP<OffsetTo<Device> > (value); }
159 static inline const OffsetTo<Device>& get_device (const Value* value)
160 { return *CastP<OffsetTo<Device> > (value); }
162 static inline const SHORT& get_short (const Value* value)
163 { return *CastP<SHORT> (value); }
167 inline bool has_device (void) const {
168 unsigned int format = *this;
169 return (format & devices) != 0;
172 inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
174 return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
177 inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
179 unsigned int len = get_len ();
181 if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
183 if (!has_device ()) return TRACE_RETURN (true);
185 for (unsigned int i = 0; i < count; i++) {
186 if (!sanitize_value_devices (c, base, values))
187 return TRACE_RETURN (false);
191 return TRACE_RETURN (true);
194 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
195 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
198 if (!has_device ()) return TRACE_RETURN (true);
200 for (unsigned int i = 0; i < count; i++) {
201 if (!sanitize_value_devices (c, base, values))
202 return TRACE_RETURN (false);
206 return TRACE_RETURN (true);
213 friend struct Anchor;
216 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
217 hb_position_t *x, hb_position_t *y) const
219 *x = font->em_scale_x (xCoordinate);
220 *y = font->em_scale_y (yCoordinate);
223 inline bool sanitize (hb_sanitize_context_t *c) {
225 return TRACE_RETURN (c->check_struct (this));
229 USHORT format; /* Format identifier--format = 1 */
230 SHORT xCoordinate; /* Horizontal value--in design units */
231 SHORT yCoordinate; /* Vertical value--in design units */
233 DEFINE_SIZE_STATIC (6);
238 friend struct Anchor;
241 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
242 hb_position_t *x, hb_position_t *y) const
244 unsigned int x_ppem = font->x_ppem;
245 unsigned int y_ppem = font->y_ppem;
246 hb_position_t cx, cy;
247 hb_bool_t ret = false;
249 if (x_ppem || y_ppem)
250 ret = hb_font_get_glyph_contour_point_for_origin (font, glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
251 *x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate);
252 *y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate);
255 inline bool sanitize (hb_sanitize_context_t *c) {
257 return TRACE_RETURN (c->check_struct (this));
261 USHORT format; /* Format identifier--format = 2 */
262 SHORT xCoordinate; /* Horizontal value--in design units */
263 SHORT yCoordinate; /* Vertical value--in design units */
264 USHORT anchorPoint; /* Index to glyph contour point */
266 DEFINE_SIZE_STATIC (8);
271 friend struct Anchor;
274 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
275 hb_position_t *x, hb_position_t *y) const
277 *x = font->em_scale_x (xCoordinate);
278 *y = font->em_scale_y (yCoordinate);
281 *x += (this+xDeviceTable).get_x_delta (font);
283 *y += (this+yDeviceTable).get_x_delta (font);
286 inline bool sanitize (hb_sanitize_context_t *c) {
288 return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
292 USHORT format; /* Format identifier--format = 3 */
293 SHORT xCoordinate; /* Horizontal value--in design units */
294 SHORT yCoordinate; /* Vertical value--in design units */
296 xDeviceTable; /* Offset to Device table for X
297 * coordinate-- from beginning of
298 * Anchor table (may be NULL) */
300 yDeviceTable; /* Offset to Device table for Y
301 * coordinate-- from beginning of
302 * Anchor table (may be NULL) */
304 DEFINE_SIZE_STATIC (10);
309 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
310 hb_position_t *x, hb_position_t *y) const
314 case 1: u.format1.get_anchor (font, glyph_id, x, y); return;
315 case 2: u.format2.get_anchor (font, glyph_id, x, y); return;
316 case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
321 inline bool sanitize (hb_sanitize_context_t *c) {
323 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
325 case 1: return TRACE_RETURN (u.format1.sanitize (c));
326 case 2: return TRACE_RETURN (u.format2.sanitize (c));
327 case 3: return TRACE_RETURN (u.format3.sanitize (c));
328 default:return TRACE_RETURN (true);
334 USHORT format; /* Format identifier */
335 AnchorFormat1 format1;
336 AnchorFormat2 format2;
337 AnchorFormat3 format3;
340 DEFINE_SIZE_UNION (2, format);
346 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
347 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
348 return this+matrix[row * cols + col];
351 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
353 if (!c->check_struct (this)) return TRACE_RETURN (false);
354 if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
355 unsigned int count = rows * cols;
356 if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
357 for (unsigned int i = 0; i < count; i++)
358 if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
359 return TRACE_RETURN (true);
362 USHORT rows; /* Number of rows */
365 matrix[VAR]; /* Matrix of offsets to Anchor tables--
366 * from beginning of AnchorMatrix table */
368 DEFINE_SIZE_ARRAY (2, matrix);
374 friend struct MarkArray;
376 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
378 return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
382 USHORT klass; /* Class defined for this mark */
384 markAnchor; /* Offset to Anchor table--from
385 * beginning of MarkArray table */
387 DEFINE_SIZE_STATIC (4);
390 struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */
392 inline bool apply (hb_apply_context_t *c,
393 unsigned int mark_index, unsigned int glyph_index,
394 const AnchorMatrix &anchors, unsigned int class_count,
395 unsigned int glyph_pos) const
398 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
399 unsigned int mark_class = record.klass;
401 const Anchor& mark_anchor = this + record.markAnchor;
402 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
404 hb_position_t mark_x, mark_y, base_x, base_y;
406 mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y);
407 glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
409 hb_glyph_position_t &o = c->buffer->cur_pos();
410 o.x_offset = base_x - mark_x;
411 o.y_offset = base_y - mark_y;
412 o.attach_lookback() = c->buffer->idx - glyph_pos;
415 return TRACE_RETURN (true);
418 inline bool sanitize (hb_sanitize_context_t *c) {
420 return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
427 struct SinglePosFormat1
429 friend struct SinglePos;
432 inline bool apply (hb_apply_context_t *c) const
435 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
436 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
438 valueFormat.apply_value (c->font, c->direction, this,
439 values, c->buffer->cur_pos());
442 return TRACE_RETURN (true);
445 inline bool sanitize (hb_sanitize_context_t *c) {
447 return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
451 USHORT format; /* Format identifier--format = 1 */
453 coverage; /* Offset to Coverage table--from
454 * beginning of subtable */
455 ValueFormat valueFormat; /* Defines the types of data in the
457 ValueRecord values; /* Defines positioning
458 * value(s)--applied to all glyphs in
459 * the Coverage table */
461 DEFINE_SIZE_ARRAY (6, values);
464 struct SinglePosFormat2
466 friend struct SinglePos;
469 inline bool apply (hb_apply_context_t *c) const
472 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
473 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
475 if (likely (index >= valueCount)) return TRACE_RETURN (false);
477 valueFormat.apply_value (c->font, c->direction, this,
478 &values[index * valueFormat.get_len ()],
479 c->buffer->cur_pos());
482 return TRACE_RETURN (true);
485 inline bool sanitize (hb_sanitize_context_t *c) {
487 return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
491 USHORT format; /* Format identifier--format = 2 */
493 coverage; /* Offset to Coverage table--from
494 * beginning of subtable */
495 ValueFormat valueFormat; /* Defines the types of data in the
497 USHORT valueCount; /* Number of ValueRecords */
498 ValueRecord values; /* Array of ValueRecords--positioning
499 * values applied to glyphs */
501 DEFINE_SIZE_ARRAY (8, values);
506 friend struct PosLookupSubTable;
509 inline bool apply (hb_apply_context_t *c) const
513 case 1: return TRACE_RETURN (u.format1.apply (c));
514 case 2: return TRACE_RETURN (u.format2.apply (c));
515 default:return TRACE_RETURN (false);
519 inline bool sanitize (hb_sanitize_context_t *c) {
521 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
523 case 1: return TRACE_RETURN (u.format1.sanitize (c));
524 case 2: return TRACE_RETURN (u.format2.sanitize (c));
525 default:return TRACE_RETURN (true);
531 USHORT format; /* Format identifier */
532 SinglePosFormat1 format1;
533 SinglePosFormat2 format2;
538 struct PairValueRecord
540 friend struct PairSet;
543 GlyphID secondGlyph; /* GlyphID of second glyph in the
544 * pair--first glyph is listed in the
546 ValueRecord values; /* Positioning data for the first glyph
547 * followed by for second glyph */
549 DEFINE_SIZE_ARRAY (2, values);
554 friend struct PairPosFormat1;
556 inline bool apply (hb_apply_context_t *c,
557 const ValueFormat *valueFormats,
558 unsigned int pos) const
561 unsigned int len1 = valueFormats[0].get_len ();
562 unsigned int len2 = valueFormats[1].get_len ();
563 unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
565 unsigned int count = len;
566 const PairValueRecord *record = CastP<PairValueRecord> (array);
567 for (unsigned int i = 0; i < count; i++)
569 if (c->buffer->info[pos].codepoint == record->secondGlyph)
571 valueFormats[0].apply_value (c->font, c->direction, this,
572 &record->values[0], c->buffer->cur_pos());
573 valueFormats[1].apply_value (c->font, c->direction, this,
574 &record->values[len1], c->buffer->pos[pos]);
577 c->buffer->idx = pos;
578 return TRACE_RETURN (true);
580 record = &StructAtOffset<PairValueRecord> (record, record_size);
583 return TRACE_RETURN (false);
586 struct sanitize_closure_t {
588 ValueFormat *valueFormats;
589 unsigned int len1; /* valueFormats[0].get_len() */
590 unsigned int stride; /* 1 + len1 + len2 */
593 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
595 if (!(c->check_struct (this)
596 && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
598 unsigned int count = len;
599 PairValueRecord *record = CastP<PairValueRecord> (array);
600 return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
601 && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
605 USHORT len; /* Number of PairValueRecords */
606 USHORT array[VAR]; /* Array of PairValueRecords--ordered
607 * by GlyphID of the second glyph */
609 DEFINE_SIZE_ARRAY (2, array);
612 struct PairPosFormat1
614 friend struct PairPos;
617 inline bool apply (hb_apply_context_t *c) const
620 hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
621 if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
623 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
624 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
626 if (!skippy_iter.next ()) return TRACE_RETURN (false);
628 return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
631 inline bool sanitize (hb_sanitize_context_t *c) {
634 unsigned int len1 = valueFormat1.get_len ();
635 unsigned int len2 = valueFormat2.get_len ();
636 PairSet::sanitize_closure_t closure = {
643 return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
647 USHORT format; /* Format identifier--format = 1 */
649 coverage; /* Offset to Coverage table--from
650 * beginning of subtable */
651 ValueFormat valueFormat1; /* Defines the types of data in
652 * ValueRecord1--for the first glyph
653 * in the pair--may be zero (0) */
654 ValueFormat valueFormat2; /* Defines the types of data in
655 * ValueRecord2--for the second glyph
656 * in the pair--may be zero (0) */
657 OffsetArrayOf<PairSet>
658 pairSet; /* Array of PairSet tables
659 * ordered by Coverage Index */
661 DEFINE_SIZE_ARRAY (10, pairSet);
664 struct PairPosFormat2
666 friend struct PairPos;
669 inline bool apply (hb_apply_context_t *c) const
672 hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
673 if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
675 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
676 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
678 if (!skippy_iter.next ()) return TRACE_RETURN (false);
680 unsigned int len1 = valueFormat1.get_len ();
681 unsigned int len2 = valueFormat2.get_len ();
682 unsigned int record_len = len1 + len2;
684 unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint);
685 unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
686 if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
688 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
689 valueFormat1.apply_value (c->font, c->direction, this,
690 v, c->buffer->cur_pos());
691 valueFormat2.apply_value (c->font, c->direction, this,
692 v + len1, c->buffer->pos[skippy_iter.idx]);
694 c->buffer->idx = skippy_iter.idx;
698 return TRACE_RETURN (true);
701 inline bool sanitize (hb_sanitize_context_t *c) {
703 if (!(c->check_struct (this)
704 && coverage.sanitize (c, this)
705 && classDef1.sanitize (c, this)
706 && classDef2.sanitize (c, this))) return TRACE_RETURN (false);
708 unsigned int len1 = valueFormat1.get_len ();
709 unsigned int len2 = valueFormat2.get_len ();
710 unsigned int stride = len1 + len2;
711 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
712 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
713 return TRACE_RETURN (c->check_array (values, record_size, count) &&
714 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
715 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
719 USHORT format; /* Format identifier--format = 2 */
721 coverage; /* Offset to Coverage table--from
722 * beginning of subtable */
723 ValueFormat valueFormat1; /* ValueRecord definition--for the
724 * first glyph of the pair--may be zero
726 ValueFormat valueFormat2; /* ValueRecord definition--for the
727 * second glyph of the pair--may be
730 classDef1; /* Offset to ClassDef table--from
731 * beginning of PairPos subtable--for
732 * the first glyph of the pair */
734 classDef2; /* Offset to ClassDef table--from
735 * beginning of PairPos subtable--for
736 * the second glyph of the pair */
737 USHORT class1Count; /* Number of classes in ClassDef1
738 * table--includes Class0 */
739 USHORT class2Count; /* Number of classes in ClassDef2
740 * table--includes Class0 */
741 ValueRecord values; /* Matrix of value pairs:
742 * class1-major, class2-minor,
743 * Each entry has value1 and value2 */
745 DEFINE_SIZE_ARRAY (16, values);
750 friend struct PosLookupSubTable;
753 inline bool apply (hb_apply_context_t *c) const
757 case 1: return TRACE_RETURN (u.format1.apply (c));
758 case 2: return TRACE_RETURN (u.format2.apply (c));
759 default:return TRACE_RETURN (false);
763 inline bool sanitize (hb_sanitize_context_t *c) {
765 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
767 case 1: return TRACE_RETURN (u.format1.sanitize (c));
768 case 2: return TRACE_RETURN (u.format2.sanitize (c));
769 default:return TRACE_RETURN (true);
775 USHORT format; /* Format identifier */
776 PairPosFormat1 format1;
777 PairPosFormat2 format2;
782 struct EntryExitRecord
784 friend struct CursivePosFormat1;
786 inline bool sanitize (hb_sanitize_context_t *c, void *base) {
788 return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
793 entryAnchor; /* Offset to EntryAnchor table--from
794 * beginning of CursivePos
795 * subtable--may be NULL */
797 exitAnchor; /* Offset to ExitAnchor table--from
798 * beginning of CursivePos
799 * subtable--may be NULL */
801 DEFINE_SIZE_STATIC (4);
804 struct CursivePosFormat1
806 friend struct CursivePos;
809 inline bool apply (hb_apply_context_t *c) const
813 /* We don't handle mark glyphs here. */
814 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
816 hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
817 if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
819 const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)];
820 if (!this_record.exitAnchor) return TRACE_RETURN (false);
822 if (!skippy_iter.next ()) return TRACE_RETURN (false);
824 const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
825 if (!next_record.entryAnchor) return TRACE_RETURN (false);
827 unsigned int i = c->buffer->idx;
828 unsigned int j = skippy_iter.idx;
830 hb_position_t entry_x, entry_y, exit_x, exit_y;
831 (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, &exit_x, &exit_y);
832 (this+next_record.entryAnchor).get_anchor (c->font, c->buffer->info[j].codepoint, &entry_x, &entry_y);
834 hb_glyph_position_t *pos = c->buffer->pos;
837 /* Main-direction adjustment */
838 switch (c->direction) {
839 case HB_DIRECTION_LTR:
840 pos[i].x_advance = exit_x + pos[i].x_offset;
842 d = entry_x + pos[j].x_offset;
843 pos[j].x_advance -= d;
844 pos[j].x_offset -= d;
846 case HB_DIRECTION_RTL:
847 d = exit_x + pos[i].x_offset;
848 pos[i].x_advance -= d;
849 pos[i].x_offset -= d;
851 pos[j].x_advance = entry_x + pos[j].x_offset;
853 case HB_DIRECTION_TTB:
854 pos[i].y_advance = exit_y + pos[i].y_offset;
856 d = entry_y + pos[j].y_offset;
857 pos[j].y_advance -= d;
858 pos[j].y_offset -= d;
860 case HB_DIRECTION_BTT:
861 d = exit_y + pos[i].y_offset;
862 pos[i].y_advance -= d;
863 pos[i].y_offset -= d;
865 pos[j].y_advance = entry_y;
867 case HB_DIRECTION_INVALID:
872 /* Cross-direction adjustment */
873 if (c->lookup_props & LookupFlag::RightToLeft) {
874 pos[i].cursive_chain() = j - i;
875 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
876 pos[i].y_offset = entry_y - exit_y;
878 pos[i].x_offset = entry_x - exit_x;
880 pos[j].cursive_chain() = i - j;
881 if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
882 pos[j].y_offset = exit_y - entry_y;
884 pos[j].x_offset = exit_x - entry_x;
888 return TRACE_RETURN (true);
891 inline bool sanitize (hb_sanitize_context_t *c) {
893 return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
897 USHORT format; /* Format identifier--format = 1 */
899 coverage; /* Offset to Coverage table--from
900 * beginning of subtable */
901 ArrayOf<EntryExitRecord>
902 entryExitRecord; /* Array of EntryExit records--in
903 * Coverage Index order */
905 DEFINE_SIZE_ARRAY (6, entryExitRecord);
910 friend struct PosLookupSubTable;
913 inline bool apply (hb_apply_context_t *c) const
917 case 1: return TRACE_RETURN (u.format1.apply (c));
918 default:return TRACE_RETURN (false);
922 inline bool sanitize (hb_sanitize_context_t *c) {
924 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
926 case 1: return TRACE_RETURN (u.format1.sanitize (c));
927 default:return TRACE_RETURN (true);
933 USHORT format; /* Format identifier */
934 CursivePosFormat1 format1;
939 typedef AnchorMatrix BaseArray; /* base-major--
940 * in order of BaseCoverage Index--,
942 * ordered by class--zero-based. */
944 struct MarkBasePosFormat1
946 friend struct MarkBasePos;
949 inline bool apply (hb_apply_context_t *c) const
952 unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
953 if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
955 /* now we search backwards for a non-mark glyph */
956 unsigned int property;
957 hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
958 if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
960 /* The following assertion is too strong, so we've disabled it. */
961 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
963 unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
964 if (base_index == NOT_COVERED) return TRACE_RETURN (false);
966 return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
969 inline bool sanitize (hb_sanitize_context_t *c) {
971 return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
972 markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
976 USHORT format; /* Format identifier--format = 1 */
978 markCoverage; /* Offset to MarkCoverage table--from
979 * beginning of MarkBasePos subtable */
981 baseCoverage; /* Offset to BaseCoverage table--from
982 * beginning of MarkBasePos subtable */
983 USHORT classCount; /* Number of classes defined for marks */
985 markArray; /* Offset to MarkArray table--from
986 * beginning of MarkBasePos subtable */
988 baseArray; /* Offset to BaseArray table--from
989 * beginning of MarkBasePos subtable */
991 DEFINE_SIZE_STATIC (12);
996 friend struct PosLookupSubTable;
999 inline bool apply (hb_apply_context_t *c) const
1003 case 1: return TRACE_RETURN (u.format1.apply (c));
1004 default:return TRACE_RETURN (false);
1008 inline bool sanitize (hb_sanitize_context_t *c) {
1010 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1012 case 1: return TRACE_RETURN (u.format1.sanitize (c));
1013 default:return TRACE_RETURN (true);
1019 USHORT format; /* Format identifier */
1020 MarkBasePosFormat1 format1;
1025 typedef AnchorMatrix LigatureAttach; /* component-major--
1026 * in order of writing direction--,
1028 * ordered by class--zero-based. */
1030 typedef OffsetListOf<LigatureAttach> LigatureArray;
1031 /* Array of LigatureAttach
1033 * LigatureCoverage Index */
1035 struct MarkLigPosFormat1
1037 friend struct MarkLigPos;
1040 inline bool apply (hb_apply_context_t *c) const
1043 unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
1044 if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
1046 /* now we search backwards for a non-mark glyph */
1047 unsigned int property;
1048 hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
1049 if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
1051 /* The following assertion is too strong, so we've disabled it. */
1052 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
1054 unsigned int j = skippy_iter.idx;
1055 unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1056 if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
1058 const LigatureArray& lig_array = this+ligatureArray;
1059 const LigatureAttach& lig_attach = lig_array[lig_index];
1061 /* Find component to attach to */
1062 unsigned int comp_count = lig_attach.rows;
1063 if (unlikely (!comp_count)) return TRACE_RETURN (false);
1065 unsigned int comp_index;
1066 /* We must now check whether the ligature ID of the current mark glyph
1067 * is identical to the ligature ID of the found ligature. If yes, we
1068 * can directly use the component index. If not, we attach the mark
1069 * glyph to the last component of the ligature. */
1070 if (get_lig_id (c->buffer->info[j]) &&
1071 get_lig_id (c->buffer->cur()) &&
1072 get_lig_comp (c->buffer->cur()) > 0)
1074 comp_index = get_lig_comp (c->buffer->cur()) - 1;
1075 if (comp_index >= comp_count)
1076 comp_index = comp_count - 1;
1079 comp_index = comp_count - 1;
1081 return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
1084 inline bool sanitize (hb_sanitize_context_t *c) {
1086 return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
1087 markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
1091 USHORT format; /* Format identifier--format = 1 */
1093 markCoverage; /* Offset to Mark Coverage table--from
1094 * beginning of MarkLigPos subtable */
1096 ligatureCoverage; /* Offset to Ligature Coverage
1097 * table--from beginning of MarkLigPos
1099 USHORT classCount; /* Number of defined mark classes */
1101 markArray; /* Offset to MarkArray table--from
1102 * beginning of MarkLigPos subtable */
1103 OffsetTo<LigatureArray>
1104 ligatureArray; /* Offset to LigatureArray table--from
1105 * beginning of MarkLigPos subtable */
1107 DEFINE_SIZE_STATIC (12);
1112 friend struct PosLookupSubTable;
1115 inline bool apply (hb_apply_context_t *c) const
1119 case 1: return TRACE_RETURN (u.format1.apply (c));
1120 default:return TRACE_RETURN (false);
1124 inline bool sanitize (hb_sanitize_context_t *c) {
1126 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1128 case 1: return TRACE_RETURN (u.format1.sanitize (c));
1129 default:return TRACE_RETURN (true);
1135 USHORT format; /* Format identifier */
1136 MarkLigPosFormat1 format1;
1141 typedef AnchorMatrix Mark2Array; /* mark2-major--
1142 * in order of Mark2Coverage Index--,
1144 * ordered by class--zero-based. */
1146 struct MarkMarkPosFormat1
1148 friend struct MarkMarkPos;
1151 inline bool apply (hb_apply_context_t *c) const
1154 unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint);
1155 if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
1157 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1158 unsigned int property;
1159 hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
1160 if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
1162 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
1164 unsigned int j = skippy_iter.idx;
1166 /* Two marks match only if they belong to the same base, or same component
1167 * of the same ligature. That is, the component numbers must match, and
1168 * if those are non-zero, the ligid number should also match. */
1169 if ((get_lig_comp (c->buffer->cur())) ||
1170 (get_lig_comp (c->buffer->info[j]) > 0 &&
1171 get_lig_id (c->buffer->cur())))
1172 return TRACE_RETURN (false);
1174 unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1175 if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
1177 return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
1180 inline bool sanitize (hb_sanitize_context_t *c) {
1182 return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
1183 mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
1184 && mark2Array.sanitize (c, this, (unsigned int) classCount));
1188 USHORT format; /* Format identifier--format = 1 */
1190 mark1Coverage; /* Offset to Combining Mark1 Coverage
1191 * table--from beginning of MarkMarkPos
1194 mark2Coverage; /* Offset to Combining Mark2 Coverage
1195 * table--from beginning of MarkMarkPos
1197 USHORT classCount; /* Number of defined mark classes */
1199 mark1Array; /* Offset to Mark1Array table--from
1200 * beginning of MarkMarkPos subtable */
1201 OffsetTo<Mark2Array>
1202 mark2Array; /* Offset to Mark2Array table--from
1203 * beginning of MarkMarkPos subtable */
1205 DEFINE_SIZE_STATIC (12);
1210 friend struct PosLookupSubTable;
1213 inline bool apply (hb_apply_context_t *c) const
1217 case 1: return TRACE_RETURN (u.format1.apply (c));
1218 default:return TRACE_RETURN (false);
1222 inline bool sanitize (hb_sanitize_context_t *c) {
1224 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
1226 case 1: return TRACE_RETURN (u.format1.sanitize (c));
1227 default:return TRACE_RETURN (true);
1233 USHORT format; /* Format identifier */
1234 MarkMarkPosFormat1 format1;
1239 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1241 struct ContextPos : Context
1243 friend struct PosLookupSubTable;
1246 inline bool apply (hb_apply_context_t *c) const
1249 return TRACE_RETURN (Context::apply (c, position_lookup));
1253 struct ChainContextPos : ChainContext
1255 friend struct PosLookupSubTable;
1258 inline bool apply (hb_apply_context_t *c) const
1261 return TRACE_RETURN (ChainContext::apply (c, position_lookup));
1266 struct ExtensionPos : Extension
1268 friend struct PosLookupSubTable;
1271 inline const struct PosLookupSubTable& get_subtable (void) const
1273 unsigned int offset = get_offset ();
1274 if (unlikely (!offset)) return Null(PosLookupSubTable);
1275 return StructAtOffset<PosLookupSubTable> (this, offset);
1278 inline bool apply (hb_apply_context_t *c) const;
1280 inline bool sanitize (hb_sanitize_context_t *c);
1290 struct PosLookupSubTable
1292 friend struct PosLookup;
1306 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1309 switch (lookup_type) {
1310 case Single: return TRACE_RETURN (u.single.apply (c));
1311 case Pair: return TRACE_RETURN (u.pair.apply (c));
1312 case Cursive: return TRACE_RETURN (u.cursive.apply (c));
1313 case MarkBase: return TRACE_RETURN (u.markBase.apply (c));
1314 case MarkLig: return TRACE_RETURN (u.markLig.apply (c));
1315 case MarkMark: return TRACE_RETURN (u.markMark.apply (c));
1316 case Context: return TRACE_RETURN (u.c.apply (c));
1317 case ChainContext: return TRACE_RETURN (u.chainContext.apply (c));
1318 case Extension: return TRACE_RETURN (u.extension.apply (c));
1319 default: return TRACE_RETURN (false);
1323 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1325 switch (lookup_type) {
1326 case Single: return TRACE_RETURN (u.single.sanitize (c));
1327 case Pair: return TRACE_RETURN (u.pair.sanitize (c));
1328 case Cursive: return TRACE_RETURN (u.cursive.sanitize (c));
1329 case MarkBase: return TRACE_RETURN (u.markBase.sanitize (c));
1330 case MarkLig: return TRACE_RETURN (u.markLig.sanitize (c));
1331 case MarkMark: return TRACE_RETURN (u.markMark.sanitize (c));
1332 case Context: return TRACE_RETURN (u.c.sanitize (c));
1333 case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c));
1334 case Extension: return TRACE_RETURN (u.extension.sanitize (c));
1335 default: return TRACE_RETURN (true);
1345 MarkBasePos markBase;
1347 MarkMarkPos markMark;
1349 ChainContextPos chainContext;
1350 ExtensionPos extension;
1353 DEFINE_SIZE_UNION (2, sub_format);
1357 struct PosLookup : Lookup
1359 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1360 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1362 inline bool apply_once (hb_apply_context_t *c) const
1364 unsigned int lookup_type = get_type ();
1366 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
1369 for (unsigned int i = 0; i < get_subtable_count (); i++)
1370 if (get_subtable (i).apply (c, lookup_type))
1376 inline bool apply_string (hb_apply_context_t *c) const
1380 if (unlikely (!c->buffer->len))
1383 c->set_lookup (*this);
1386 while (c->buffer->idx < c->buffer->len)
1388 if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
1397 inline bool sanitize (hb_sanitize_context_t *c) {
1399 if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
1400 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1401 return TRACE_RETURN (list.sanitize (c, this, get_type ()));
1405 typedef OffsetListOf<PosLookup> PosLookupList;
1408 * GPOS -- The Glyph Positioning Table
1411 struct GPOS : GSUBGPOS
1413 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1415 inline const PosLookup& get_lookup (unsigned int i) const
1416 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1418 inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
1419 { return get_lookup (lookup_index).apply_string (c); }
1421 static inline void position_start (hb_buffer_t *buffer);
1422 static inline void position_finish (hb_buffer_t *buffer);
1424 inline bool sanitize (hb_sanitize_context_t *c) {
1426 if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
1427 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1428 return TRACE_RETURN (list.sanitize (c, this));
1431 DEFINE_SIZE_STATIC (10);
1436 fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
1438 unsigned int j = pos[i].cursive_chain();
1444 pos[i].cursive_chain() = 0;
1446 fix_cursive_minor_offset (pos, j, direction);
1448 if (HB_DIRECTION_IS_HORIZONTAL (direction))
1449 pos[i].y_offset += pos[j].y_offset;
1451 pos[i].x_offset += pos[j].x_offset;
1455 fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
1457 if (likely (!(pos[i].attach_lookback())))
1460 unsigned int j = i - pos[i].attach_lookback();
1462 pos[i].x_advance = 0;
1463 pos[i].y_advance = 0;
1464 pos[i].x_offset += pos[j].x_offset;
1465 pos[i].y_offset += pos[j].y_offset;
1467 if (HB_DIRECTION_IS_FORWARD (direction))
1468 for (unsigned int k = j; k < i; k++) {
1469 pos[i].x_offset -= pos[k].x_advance;
1470 pos[i].y_offset -= pos[k].y_advance;
1473 for (unsigned int k = j + 1; k < i + 1; k++) {
1474 pos[i].x_offset += pos[k].x_advance;
1475 pos[i].y_offset += pos[k].y_advance;
1480 GPOS::position_start (hb_buffer_t *buffer)
1482 buffer->clear_positions ();
1484 unsigned int count = buffer->len;
1485 for (unsigned int i = 0; i < count; i++)
1486 buffer->pos[i].attach_lookback() = buffer->pos[i].cursive_chain() = 0;
1490 GPOS::position_finish (hb_buffer_t *buffer)
1493 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
1494 hb_direction_t direction = buffer->props.direction;
1496 /* Handle cursive connections */
1497 for (unsigned int i = 0; i < len; i++)
1498 fix_cursive_minor_offset (pos, i, direction);
1500 /* Handle attachments */
1501 for (unsigned int i = 0; i < len; i++)
1502 fix_mark_attachment (pos, i, direction);
1504 HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
1505 HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
1506 HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
1510 /* Out-of-class implementation for methods recursing */
1512 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1515 return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
1518 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1521 if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
1522 unsigned int offset = get_offset ();
1523 if (unlikely (!offset)) return TRACE_RETURN (true);
1524 return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
1527 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1529 const GPOS &gpos = *(c->face->ot_layout->gpos);
1530 const PosLookup &l = gpos.get_lookup (lookup_index);
1532 if (unlikely (c->nesting_level_left == 0))
1535 hb_apply_context_t new_c (*c);
1536 new_c.nesting_level_left--;
1537 new_c.set_lookup (l);
1538 return l.apply_once (&new_c);
1542 #undef attach_lookback
1543 #undef cursive_chain
1547 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */