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 *layout_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 = layout_context->font->x_scale;
102 y_scale = layout_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 if (!has_device ()) return;
111 x_ppem = layout_context->font->x_ppem;
112 y_ppem = layout_context->font->y_ppem;
114 if (!x_ppem && !y_ppem) return;
116 /* pixel -> fractional pixel */
117 if (format & xPlaDevice) {
118 if (x_ppem) glyph_pos->x_offset += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
120 if (format & yPlaDevice) {
121 if (y_ppem) glyph_pos->y_offset += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
123 if (format & xAdvDevice) {
124 if (x_ppem) glyph_pos->x_advance += (base+*(OffsetTo<Device>*)values++).get_delta (x_ppem) << 16; else values++;
126 if (format & yAdvDevice) {
127 if (y_ppem) glyph_pos->y_advance += (base+*(OffsetTo<Device>*)values++).get_delta (y_ppem) << 16; else values++;
132 inline bool sanitize_value_devices (SANITIZE_ARG_DEF, void *base, const Value *values) {
133 unsigned int format = *this;
135 if (format & xPlacement) values++;
136 if (format & yPlacement) values++;
137 if (format & xAdvance) values++;
138 if (format & yAdvance) values++;
140 if ((format & xPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
141 if ((format & yPlaDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
142 if ((format & xAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
143 if ((format & yAdvDevice) && !SANITIZE_BASE (*(OffsetTo<Device>*)values++, base)) return false;
150 inline bool has_device () const {
151 unsigned int format = *this;
152 return (format & devices) != 0;
155 inline bool sanitize_value (SANITIZE_ARG_DEF, void *base, const Value *values) {
158 return SANITIZE_MEM (values, get_size ()) &&
159 (!has_device () || sanitize_value_devices (SANITIZE_ARG, base, values));
162 inline bool sanitize_values (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count) {
164 unsigned int len = get_len ();
166 if (!SANITIZE_ARRAY (values, get_size (), count)) return false;
168 if (!has_device ()) return true;
170 for (unsigned int i = 0; i < count; i++) {
171 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
179 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
180 inline bool sanitize_values_stride_unsafe (SANITIZE_ARG_DEF, void *base, const Value *values, unsigned int count, unsigned int stride) {
183 if (!has_device ()) return true;
185 for (unsigned int i = 0; i < count; i++) {
186 if (!sanitize_value_devices (SANITIZE_ARG, base, values))
194 ASSERT_SIZE (ValueFormat, 2);
199 friend struct Anchor;
202 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
203 hb_position_t *x, hb_position_t *y) const
205 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
206 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
209 inline bool sanitize (SANITIZE_ARG_DEF) {
211 return SANITIZE_SELF ();
215 USHORT format; /* Format identifier--format = 1 */
216 SHORT xCoordinate; /* Horizontal value--in design units */
217 SHORT yCoordinate; /* Vertical value--in design units */
219 ASSERT_SIZE (AnchorFormat1, 6);
223 friend struct Anchor;
226 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
227 hb_position_t *x, hb_position_t *y) const
229 unsigned int x_ppem = layout_context->font->x_ppem;
230 unsigned int y_ppem = layout_context->font->y_ppem;
231 hb_position_t cx, cy;
234 if (x_ppem || y_ppem)
235 ret = hb_font_get_contour_point (layout_context->font, layout_context->face, anchorPoint, glyph_id, &cx, &cy);
236 *x = x_ppem && ret ? cx : _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
237 *y = y_ppem && ret ? cy : _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
240 inline bool sanitize (SANITIZE_ARG_DEF) {
242 return SANITIZE_SELF ();
246 USHORT format; /* Format identifier--format = 2 */
247 SHORT xCoordinate; /* Horizontal value--in design units */
248 SHORT yCoordinate; /* Vertical value--in design units */
249 USHORT anchorPoint; /* Index to glyph contour point */
251 ASSERT_SIZE (AnchorFormat2, 8);
255 friend struct Anchor;
258 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id HB_UNUSED,
259 hb_position_t *x, hb_position_t *y) const
261 *x = _hb_16dot16_mul_round (layout_context->font->x_scale, xCoordinate);
262 *y = _hb_16dot16_mul_round (layout_context->font->y_scale, yCoordinate);
264 /* pixel -> fractional pixel */
265 if (layout_context->font->x_ppem)
266 *x += (this+xDeviceTable).get_delta (layout_context->font->x_ppem) << 16;
267 if (layout_context->font->y_ppem)
268 *y += (this+yDeviceTable).get_delta (layout_context->font->y_ppem) << 16;
271 inline bool sanitize (SANITIZE_ARG_DEF) {
273 return SANITIZE_SELF () && SANITIZE_THIS2 (xDeviceTable, yDeviceTable);
277 USHORT format; /* Format identifier--format = 3 */
278 SHORT xCoordinate; /* Horizontal value--in design units */
279 SHORT yCoordinate; /* Vertical value--in design units */
281 xDeviceTable; /* Offset to Device table for X
282 * coordinate-- from beginning of
283 * Anchor table (may be NULL) */
285 yDeviceTable; /* Offset to Device table for Y
286 * coordinate-- from beginning of
287 * Anchor table (may be NULL) */
289 ASSERT_SIZE (AnchorFormat3, 10);
293 inline void get_anchor (hb_ot_layout_context_t *layout_context, hb_codepoint_t glyph_id,
294 hb_position_t *x, hb_position_t *y) const
298 case 1: u.format1->get_anchor (layout_context, glyph_id, x, y); return;
299 case 2: u.format2->get_anchor (layout_context, glyph_id, x, y); return;
300 case 3: u.format3->get_anchor (layout_context, glyph_id, x, y); return;
305 inline bool sanitize (SANITIZE_ARG_DEF) {
307 if (!SANITIZE (u.format)) return false;
309 case 1: return u.format1->sanitize (SANITIZE_ARG);
310 case 2: return u.format2->sanitize (SANITIZE_ARG);
311 case 3: return u.format3->sanitize (SANITIZE_ARG);
318 USHORT format; /* Format identifier */
319 AnchorFormat1 format1[VAR];
320 AnchorFormat2 format2[VAR];
321 AnchorFormat3 format3[VAR];
328 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
329 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
330 return this+matrix[row * cols + col];
333 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int cols) {
335 if (!SANITIZE_SELF ()) return false;
336 if (unlikely (cols >= ((unsigned int) -1) / rows)) return false;
337 unsigned int count = rows * cols;
338 if (!SANITIZE_ARRAY (matrix, matrix[0].get_size (), count)) return false;
339 for (unsigned int i = 0; i < count; i++)
340 if (!SANITIZE_THIS (matrix[i])) return false;
344 USHORT rows; /* Number of rows */
347 matrix[VAR]; /* Matrix of offsets to Anchor tables--
348 * from beginning of AnchorMatrix table */
350 ASSERT_SIZE_VAR (AnchorMatrix, 2, OffsetTo<Anchor>);
355 friend struct MarkArray;
357 static inline unsigned int get_size () { return sizeof (MarkRecord); }
359 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
361 return SANITIZE_SELF () && SANITIZE_BASE (markAnchor, base);
365 USHORT klass; /* Class defined for this mark */
367 markAnchor; /* Offset to Anchor table--from
368 * beginning of MarkArray table */
370 ASSERT_SIZE (MarkRecord, 4);
374 inline bool apply (APPLY_ARG_DEF,
375 unsigned int mark_index, unsigned int glyph_index,
376 const AnchorMatrix &anchors, unsigned int class_count,
377 unsigned int glyph_pos) const
380 const MarkRecord &record = markRecord[mark_index];
381 unsigned int mark_class = record.klass;
383 const Anchor& mark_anchor = this + record.markAnchor;
384 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
386 hb_position_t mark_x, mark_y, base_x, base_y;
388 mark_anchor.get_anchor (layout_context, IN_CURGLYPH (), &mark_x, &mark_y);
389 glyph_anchor.get_anchor (layout_context, IN_GLYPH (glyph_pos), &base_x, &base_y);
391 hb_internal_glyph_position_t *o = POSITION (buffer->in_pos);
394 o->x_offset = base_x - mark_x;
395 o->y_offset = base_y - mark_y;
396 o->back = buffer->in_pos - glyph_pos;
402 inline bool sanitize (SANITIZE_ARG_DEF) {
404 return SANITIZE_THIS (markRecord);
409 markRecord; /* Array of MarkRecords--in Coverage order */
411 ASSERT_SIZE (MarkArray, 2);
416 struct SinglePosFormat1
418 friend struct SinglePos;
421 inline bool apply (APPLY_ARG_DEF) const
424 unsigned int index = (this+coverage) (IN_CURGLYPH ());
425 if (likely (index == NOT_COVERED))
428 valueFormat.apply_value (layout_context, CharP(this), values, CURPOSITION ());
434 inline bool sanitize (SANITIZE_ARG_DEF) {
436 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
437 valueFormat.sanitize_value (SANITIZE_ARG, CharP(this), values);
441 USHORT format; /* Format identifier--format = 1 */
443 coverage; /* Offset to Coverage table--from
444 * beginning of subtable */
445 ValueFormat valueFormat; /* Defines the types of data in the
447 ValueRecord values; /* Defines positioning
448 * value(s)--applied to all glyphs in
449 * the Coverage table */
451 ASSERT_SIZE_VAR (SinglePosFormat1, 6, ValueRecord);
453 struct SinglePosFormat2
455 friend struct SinglePos;
458 inline bool apply (APPLY_ARG_DEF) const
461 unsigned int index = (this+coverage) (IN_CURGLYPH ());
462 if (likely (index == NOT_COVERED))
465 if (likely (index >= valueCount))
468 valueFormat.apply_value (layout_context, CharP(this),
469 &values[index * valueFormat.get_len ()],
476 inline bool sanitize (SANITIZE_ARG_DEF) {
478 return SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
479 valueFormat.sanitize_values (SANITIZE_ARG, CharP(this), values, valueCount);
483 USHORT format; /* Format identifier--format = 2 */
485 coverage; /* Offset to Coverage table--from
486 * beginning of subtable */
487 ValueFormat valueFormat; /* Defines the types of data in the
489 USHORT valueCount; /* Number of ValueRecords */
490 ValueRecord values; /* Array of ValueRecords--positioning
491 * values applied to glyphs */
493 ASSERT_SIZE_VAR (SinglePosFormat2, 8, ValueRecord);
497 friend struct PosLookupSubTable;
500 inline bool apply (APPLY_ARG_DEF) const
504 case 1: return u.format1->apply (APPLY_ARG);
505 case 2: return u.format2->apply (APPLY_ARG);
506 default:return false;
510 inline bool sanitize (SANITIZE_ARG_DEF) {
512 if (!SANITIZE (u.format)) return false;
514 case 1: return u.format1->sanitize (SANITIZE_ARG);
515 case 2: return u.format2->sanitize (SANITIZE_ARG);
522 USHORT format; /* Format identifier */
523 SinglePosFormat1 format1[VAR];
524 SinglePosFormat2 format2[VAR];
529 struct PairValueRecord
531 friend struct PairPosFormat1;
534 GlyphID secondGlyph; /* GlyphID of second glyph in the
535 * pair--first glyph is listed in the
537 ValueRecord values; /* Positioning data for the first glyph
538 * followed by for second glyph */
540 ASSERT_SIZE_VAR (PairValueRecord, 2, ValueRecord);
544 friend struct PairPosFormat1;
546 /* Note: Doesn't sanitize the Device entries in the ValueRecord */
547 inline bool sanitize (SANITIZE_ARG_DEF, unsigned int format_len) {
549 if (!SANITIZE_SELF ()) return false;
550 unsigned int count = (1 + format_len) * len;
551 return SANITIZE_ARRAY (array, USHORT::get_size (), count);
555 USHORT len; /* Number of PairValueRecords */
557 array[VAR]; /* Array of PairValueRecords--ordered
558 * by GlyphID of the second glyph */
560 ASSERT_SIZE_VAR (PairSet, 2, PairValueRecord);
562 struct PairPosFormat1
564 friend struct PairPos;
567 inline bool apply (APPLY_ARG_DEF) const
570 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
571 if (unlikely (buffer->in_pos + 2 > end))
574 unsigned int index = (this+coverage) (IN_CURGLYPH ());
575 if (likely (index == NOT_COVERED))
578 unsigned int j = buffer->in_pos + 1;
579 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
581 if (unlikely (j == end))
586 unsigned int len1 = valueFormat1.get_len ();
587 unsigned int len2 = valueFormat2.get_len ();
588 unsigned int record_size = USHORT::get_size () * (1 + len1 + len2);
590 const PairSet &pair_set = this+pairSet[index];
591 unsigned int count = pair_set.len;
592 const PairValueRecord *record = pair_set.array;
593 for (unsigned int i = 0; i < count; i++)
595 if (IN_GLYPH (j) == record->secondGlyph)
597 valueFormat1.apply_value (layout_context, CharP(this), &record->values[0], CURPOSITION ());
598 valueFormat2.apply_value (layout_context, CharP(this), &record->values[len1], POSITION (j));
604 record = &StructAtOffset<PairValueRecord> (*record, record_size);
610 inline bool sanitize (SANITIZE_ARG_DEF) {
613 unsigned int len1 = valueFormat1.get_len ();
614 unsigned int len2 = valueFormat2.get_len ();
616 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
617 likely (pairSet.sanitize (SANITIZE_ARG, CharP(this), len1 + len2)))) return false;
619 if (!(valueFormat1.has_device () || valueFormat2.has_device ())) return true;
621 unsigned int stride = 1 + len1 + len2;
622 unsigned int count1 = pairSet.len;
623 for (unsigned int i = 0; i < count1; i++)
625 const PairSet &pair_set = this+pairSet[i];
627 unsigned int count2 = pair_set.len;
628 const PairValueRecord *record = pair_set.array;
629 if (!(valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[0], count2, stride) &&
630 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &record->values[len1], count2, stride)))
638 USHORT format; /* Format identifier--format = 1 */
640 coverage; /* Offset to Coverage table--from
641 * beginning of subtable */
642 ValueFormat valueFormat1; /* Defines the types of data in
643 * ValueRecord1--for the first glyph
644 * in the pair--may be zero (0) */
645 ValueFormat valueFormat2; /* Defines the types of data in
646 * ValueRecord2--for the second glyph
647 * in the pair--may be zero (0) */
648 OffsetArrayOf<PairSet>
649 pairSet; /* Array of PairSet tables
650 * ordered by Coverage Index */
652 ASSERT_SIZE (PairPosFormat1, 10);
654 struct PairPosFormat2
656 friend struct PairPos;
659 inline bool apply (APPLY_ARG_DEF) const
662 unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
663 if (unlikely (buffer->in_pos + 2 > end))
666 unsigned int index = (this+coverage) (IN_CURGLYPH ());
667 if (likely (index == NOT_COVERED))
670 unsigned int j = buffer->in_pos + 1;
671 while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, NULL))
673 if (unlikely (j == end))
678 unsigned int len1 = valueFormat1.get_len ();
679 unsigned int len2 = valueFormat2.get_len ();
680 unsigned int record_len = len1 + len2;
682 unsigned int klass1 = (this+classDef1) (IN_CURGLYPH ());
683 unsigned int klass2 = (this+classDef2) (IN_GLYPH (j));
684 if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
687 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
688 valueFormat1.apply_value (layout_context, CharP(this), v, CURPOSITION ());
689 valueFormat2.apply_value (layout_context, CharP(this), v + len1, POSITION (j));
698 inline bool sanitize (SANITIZE_ARG_DEF) {
700 if (!(SANITIZE_SELF () && SANITIZE_THIS (coverage) &&
701 SANITIZE_THIS2 (classDef1, classDef2))) return false;
703 unsigned int len1 = valueFormat1.get_len ();
704 unsigned int len2 = valueFormat2.get_len ();
705 unsigned int stride = len1 + len2;
706 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
707 unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
708 return SANITIZE_ARRAY (values, record_size, count) &&
709 valueFormat1.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[0], count, stride) &&
710 valueFormat2.sanitize_values_stride_unsafe (SANITIZE_ARG, CharP(this), &values[len1], count, stride);
714 USHORT format; /* Format identifier--format = 2 */
716 coverage; /* Offset to Coverage table--from
717 * beginning of subtable */
718 ValueFormat valueFormat1; /* ValueRecord definition--for the
719 * first glyph of the pair--may be zero
721 ValueFormat valueFormat2; /* ValueRecord definition--for the
722 * second glyph of the pair--may be
725 classDef1; /* Offset to ClassDef table--from
726 * beginning of PairPos subtable--for
727 * the first glyph of the pair */
729 classDef2; /* Offset to ClassDef table--from
730 * beginning of PairPos subtable--for
731 * the second glyph of the pair */
732 USHORT class1Count; /* Number of classes in ClassDef1
733 * table--includes Class0 */
734 USHORT class2Count; /* Number of classes in ClassDef2
735 * table--includes Class0 */
736 ValueRecord values; /* Matrix of value pairs:
737 * class1-major, class2-minor,
738 * Each entry has value1 and value2 */
740 ASSERT_SIZE_VAR (PairPosFormat2, 16, ValueRecord);
744 friend struct PosLookupSubTable;
747 inline bool apply (APPLY_ARG_DEF) const
751 case 1: return u.format1->apply (APPLY_ARG);
752 case 2: return u.format2->apply (APPLY_ARG);
753 default:return false;
757 inline bool sanitize (SANITIZE_ARG_DEF) {
759 if (!SANITIZE (u.format)) return false;
761 case 1: return u.format1->sanitize (SANITIZE_ARG);
762 case 2: return u.format2->sanitize (SANITIZE_ARG);
769 USHORT format; /* Format identifier */
770 PairPosFormat1 format1[VAR];
771 PairPosFormat2 format2[VAR];
776 struct EntryExitRecord
778 static inline unsigned int get_size () { return sizeof (EntryExitRecord); }
780 inline bool sanitize (SANITIZE_ARG_DEF, void *base) {
782 return SANITIZE_BASE (entryAnchor, base)
783 && SANITIZE_BASE (exitAnchor, base);
787 entryAnchor; /* Offset to EntryAnchor table--from
788 * beginning of CursivePos
789 * subtable--may be NULL */
791 exitAnchor; /* Offset to ExitAnchor table--from
792 * beginning of CursivePos
793 * subtable--may be NULL */
795 ASSERT_SIZE (EntryExitRecord, 4);
797 struct CursivePosFormat1
799 friend struct CursivePos;
802 inline bool apply (APPLY_ARG_DEF) const
805 /* Now comes the messiest part of the whole OpenType
806 specification. At first glance, cursive connections seem easy
807 to understand, but there are pitfalls! The reason is that
808 the specs don't mention how to compute the advance values
809 resp. glyph offsets. I was told it would be an omission, to
810 be fixed in the next OpenType version... Again many thanks to
811 Andrei Burago <andreib@microsoft.com> for clarifications.
813 Consider the following example:
826 glyph1: advance width = 12
829 glyph2: advance width = 11
832 LSB is 1 for both glyphs (so the boxes drawn above are glyph
833 bboxes). Writing direction is R2L; `0' denotes the glyph's
836 Now the surprising part: The advance width of the *left* glyph
837 (resp. of the *bottom* glyph) will be modified, no matter
838 whether the writing direction is L2R or R2L (resp. T2B or
839 B2T)! This assymetry is caused by the fact that the glyph's
840 coordinate origin is always the lower left corner for all
843 Continuing the above example, we can compute the new
844 (horizontal) advance width of glyph2 as
848 and the new vertical offset of glyph2 as
853 Vertical writing direction is far more complicated:
855 a) Assuming that we recompute the advance height of the lower glyph:
862 yadv2 | 0+--+------+ -- BSB1 --
865 BSB2 -- 0+--------+ --
868 glyph1: advance height = 6
871 glyph2: advance height = 7
874 TSB is 1 for both glyphs; writing direction is T2B.
877 BSB1 = yadv1 - (TSB1 + ymax1)
878 BSB2 = yadv2 - (TSB2 + ymax2)
881 vertical advance width of glyph2
882 = y_offset + BSB2 - BSB1
883 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
884 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
885 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
888 b) Assuming that we recompute the advance height of the upper glyph:
893 TSB2 -- +-----+--+ 1 | yadv1 ymax1
895 yadv2 | 0+--+------+ -- --
896 ymax2 | 2 | -- y_offset
901 glyph1: advance height = 6
904 glyph2: advance height = 7
907 TSB is 1 for both glyphs; writing direction is T2B.
911 vertical advance width of glyph2
912 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
913 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
916 Comparing a) with b) shows that b) is easier to compute. I'll wait
917 for a reply from Andrei to see what should really be implemented...
919 Since horizontal advance widths or vertical advance heights
920 can be used alone but not together, no ambiguity occurs. */
922 struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &layout_context->info.gpos;
923 hb_codepoint_t last_pos = gpi->last;
924 gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
926 /* We don't handle mark glyphs here. */
927 if (context->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
930 unsigned int index = (this+coverage) (IN_CURGLYPH ());
931 if (likely (index == NOT_COVERED))
934 const EntryExitRecord &record = entryExitRecord[index];
936 if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
939 hb_position_t entry_x, entry_y;
940 (this+record.entryAnchor).get_anchor (layout_context, IN_CURGLYPH (), &entry_x, &entry_y);
944 if (buffer->direction == HB_DIRECTION_RTL)
946 /* advance is absolute, not relative */
947 POSITION (buffer->in_pos)->x_advance = entry_x - gpi->anchor_x;
951 /* advance is absolute, not relative */
952 POSITION (last_pos)->x_advance = gpi->anchor_x - entry_x;
955 if (context->lookup_flag & LookupFlag::RightToLeft)
957 POSITION (last_pos)->cursive_chain = last_pos - buffer->in_pos;
958 POSITION (last_pos)->y_offset = entry_y - gpi->anchor_y;
962 POSITION (buffer->in_pos)->cursive_chain = buffer->in_pos - last_pos;
963 POSITION (buffer->in_pos)->y_offset = gpi->anchor_y - entry_y;
967 if (record.exitAnchor)
969 gpi->last = buffer->in_pos;
970 (this+record.exitAnchor).get_anchor (layout_context, IN_CURGLYPH (), &gpi->anchor_x, &gpi->anchor_y);
977 inline bool sanitize (SANITIZE_ARG_DEF) {
979 return SANITIZE_THIS2 (coverage, entryExitRecord);
983 USHORT format; /* Format identifier--format = 1 */
985 coverage; /* Offset to Coverage table--from
986 * beginning of subtable */
987 ArrayOf<EntryExitRecord>
988 entryExitRecord; /* Array of EntryExit records--in
989 * Coverage Index order */
991 ASSERT_SIZE (CursivePosFormat1, 6);
995 friend struct PosLookupSubTable;
998 inline bool apply (APPLY_ARG_DEF) const
1002 case 1: return u.format1->apply (APPLY_ARG);
1003 default:return false;
1007 inline bool sanitize (SANITIZE_ARG_DEF) {
1009 if (!SANITIZE (u.format)) return false;
1011 case 1: return u.format1->sanitize (SANITIZE_ARG);
1012 default:return true;
1018 USHORT format; /* Format identifier */
1019 CursivePosFormat1 format1[VAR];
1024 typedef AnchorMatrix BaseArray; /* base-major--
1025 * in order of BaseCoverage Index--,
1027 * ordered by class--zero-based. */
1029 struct MarkBasePosFormat1
1031 friend struct MarkBasePos;
1034 inline bool apply (APPLY_ARG_DEF) const
1037 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1038 if (likely (mark_index == NOT_COVERED))
1041 /* now we search backwards for a non-mark glyph */
1042 unsigned int property;
1043 unsigned int j = buffer->in_pos;
1049 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1052 /* The following assertion is too strong. */
1053 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1057 unsigned int base_index = (this+baseCoverage) (IN_GLYPH (j));
1058 if (base_index == NOT_COVERED)
1061 return (this+markArray).apply (APPLY_ARG, mark_index, base_index, this+baseArray, classCount, j);
1064 inline bool sanitize (SANITIZE_ARG_DEF) {
1066 return SANITIZE_SELF ()
1067 && SANITIZE_THIS (markCoverage)
1068 && SANITIZE_THIS (baseCoverage)
1069 && SANITIZE_THIS (markArray)
1070 && likely (baseArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1074 USHORT format; /* Format identifier--format = 1 */
1076 markCoverage; /* Offset to MarkCoverage table--from
1077 * beginning of MarkBasePos subtable */
1079 baseCoverage; /* Offset to BaseCoverage table--from
1080 * beginning of MarkBasePos subtable */
1081 USHORT classCount; /* Number of classes defined for marks */
1083 markArray; /* Offset to MarkArray table--from
1084 * beginning of MarkBasePos subtable */
1086 baseArray; /* Offset to BaseArray table--from
1087 * beginning of MarkBasePos subtable */
1089 ASSERT_SIZE (MarkBasePosFormat1, 12);
1093 friend struct PosLookupSubTable;
1096 inline bool apply (APPLY_ARG_DEF) const
1100 case 1: return u.format1->apply (APPLY_ARG);
1101 default:return false;
1105 inline bool sanitize (SANITIZE_ARG_DEF) {
1107 if (!SANITIZE (u.format)) return false;
1109 case 1: return u.format1->sanitize (SANITIZE_ARG);
1110 default:return true;
1116 USHORT format; /* Format identifier */
1117 MarkBasePosFormat1 format1[VAR];
1122 typedef AnchorMatrix LigatureAttach; /* component-major--
1123 * in order of writing direction--,
1125 * ordered by class--zero-based. */
1127 typedef OffsetListOf<LigatureAttach> LigatureArray;
1128 /* Array of LigatureAttach
1130 * LigatureCoverage Index */
1132 struct MarkLigPosFormat1
1134 friend struct MarkLigPos;
1137 inline bool apply (APPLY_ARG_DEF) const
1140 unsigned int mark_index = (this+markCoverage) (IN_CURGLYPH ());
1141 if (likely (mark_index == NOT_COVERED))
1144 /* now we search backwards for a non-mark glyph */
1145 unsigned int property;
1146 unsigned int j = buffer->in_pos;
1152 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), LookupFlag::IgnoreMarks, &property));
1155 /* The following assertion is too strong. */
1156 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1160 unsigned int lig_index = (this+ligatureCoverage) (IN_GLYPH (j));
1161 if (lig_index == NOT_COVERED)
1164 const LigatureArray& lig_array = this+ligatureArray;
1165 const LigatureAttach& lig_attach = lig_array[lig_index];
1167 /* Find component to attach to */
1168 unsigned int comp_count = lig_attach.rows;
1169 if (unlikely (!comp_count))
1171 unsigned int comp_index;
1172 /* We must now check whether the ligature ID of the current mark glyph
1173 * is identical to the ligature ID of the found ligature. If yes, we
1174 * can directly use the component index. If not, we attach the mark
1175 * glyph to the last component of the ligature. */
1176 if (IN_LIGID (j) && IN_LIGID (j) == IN_LIGID (buffer->in_pos) && IN_COMPONENT (buffer->in_pos))
1178 comp_index = IN_COMPONENT (buffer->in_pos) - 1;
1179 if (comp_index >= comp_count)
1180 comp_index = comp_count - 1;
1183 comp_index = comp_count - 1;
1185 return (this+markArray).apply (APPLY_ARG, mark_index, comp_index, lig_attach, classCount, j);
1188 inline bool sanitize (SANITIZE_ARG_DEF) {
1190 return SANITIZE_SELF ()
1191 && SANITIZE_THIS (markCoverage)
1192 && SANITIZE_THIS (ligatureCoverage)
1193 && SANITIZE_THIS (markArray)
1194 && likely (ligatureArray.sanitize (SANITIZE_ARG, CharP(this), classCount));
1198 USHORT format; /* Format identifier--format = 1 */
1200 markCoverage; /* Offset to Mark Coverage table--from
1201 * beginning of MarkLigPos subtable */
1203 ligatureCoverage; /* Offset to Ligature Coverage
1204 * table--from beginning of MarkLigPos
1206 USHORT classCount; /* Number of defined mark classes */
1208 markArray; /* Offset to MarkArray table--from
1209 * beginning of MarkLigPos subtable */
1210 OffsetTo<LigatureArray>
1211 ligatureArray; /* Offset to LigatureArray table--from
1212 * beginning of MarkLigPos subtable */
1214 ASSERT_SIZE (MarkLigPosFormat1, 12);
1218 friend struct PosLookupSubTable;
1221 inline bool apply (APPLY_ARG_DEF) const
1225 case 1: return u.format1->apply (APPLY_ARG);
1226 default:return false;
1230 inline bool sanitize (SANITIZE_ARG_DEF) {
1232 if (!SANITIZE (u.format)) return false;
1234 case 1: return u.format1->sanitize (SANITIZE_ARG);
1235 default:return true;
1241 USHORT format; /* Format identifier */
1242 MarkLigPosFormat1 format1[VAR];
1247 typedef AnchorMatrix Mark2Array; /* mark2-major--
1248 * in order of Mark2Coverage Index--,
1250 * ordered by class--zero-based. */
1252 struct MarkMarkPosFormat1
1254 friend struct MarkMarkPos;
1257 inline bool apply (APPLY_ARG_DEF) const
1260 unsigned int mark1_index = (this+mark1Coverage) (IN_CURGLYPH ());
1261 if (likely (mark1_index == NOT_COVERED))
1264 /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1265 unsigned int property;
1266 unsigned int j = buffer->in_pos;
1272 } while (_hb_ot_layout_skip_mark (layout_context->face, IN_INFO (j), context->lookup_flag, &property));
1274 if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1277 /* Two marks match only if they belong to the same base, or same component
1278 * of the same ligature. That is, the component numbers must match, and
1279 * if those are non-zero, the ligid number should also match. */
1280 if ((IN_COMPONENT (j) != IN_COMPONENT (buffer->in_pos)) ||
1281 (IN_COMPONENT (j) && IN_LIGID (j) != IN_LIGID (buffer->in_pos)))
1284 unsigned int mark2_index = (this+mark2Coverage) (IN_GLYPH (j));
1285 if (mark2_index == NOT_COVERED)
1288 return (this+mark1Array).apply (APPLY_ARG, mark1_index, mark2_index, this+mark2Array, classCount, j);
1291 inline bool sanitize (SANITIZE_ARG_DEF) {
1293 return SANITIZE_SELF ()
1294 && SANITIZE_THIS (mark1Coverage)
1295 && SANITIZE_THIS (mark2Coverage)
1296 && SANITIZE_THIS (mark1Array)
1297 && likely (mark2Array.sanitize (SANITIZE_ARG, CharP(this), classCount));
1301 USHORT format; /* Format identifier--format = 1 */
1303 mark1Coverage; /* Offset to Combining Mark1 Coverage
1304 * table--from beginning of MarkMarkPos
1307 mark2Coverage; /* Offset to Combining Mark2 Coverage
1308 * table--from beginning of MarkMarkPos
1310 USHORT classCount; /* Number of defined mark classes */
1312 mark1Array; /* Offset to Mark1Array table--from
1313 * beginning of MarkMarkPos subtable */
1314 OffsetTo<Mark2Array>
1315 mark2Array; /* Offset to Mark2Array table--from
1316 * beginning of MarkMarkPos subtable */
1318 ASSERT_SIZE (MarkMarkPosFormat1, 12);
1322 friend struct PosLookupSubTable;
1325 inline bool apply (APPLY_ARG_DEF) const
1329 case 1: return u.format1->apply (APPLY_ARG);
1330 default:return false;
1334 inline bool sanitize (SANITIZE_ARG_DEF) {
1336 if (!SANITIZE (u.format)) return false;
1338 case 1: return u.format1->sanitize (SANITIZE_ARG);
1339 default:return true;
1345 USHORT format; /* Format identifier */
1346 MarkMarkPosFormat1 format1[VAR];
1351 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
1353 struct ContextPos : Context
1355 friend struct PosLookupSubTable;
1358 inline bool apply (APPLY_ARG_DEF) const
1361 return Context::apply (APPLY_ARG, position_lookup);
1365 struct ChainContextPos : ChainContext
1367 friend struct PosLookupSubTable;
1370 inline bool apply (APPLY_ARG_DEF) const
1373 return ChainContext::apply (APPLY_ARG, position_lookup);
1378 struct ExtensionPos : Extension
1380 friend struct PosLookupSubTable;
1383 inline const struct PosLookupSubTable& get_subtable (void) const
1385 unsigned int offset = get_offset ();
1386 if (unlikely (!offset)) return Null(PosLookupSubTable);
1387 return StructAtOffset<PosLookupSubTable> (*this, offset);
1390 inline bool apply (APPLY_ARG_DEF) const;
1392 inline bool sanitize (SANITIZE_ARG_DEF);
1402 struct PosLookupSubTable
1404 friend struct PosLookup;
1418 inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
1421 switch (lookup_type) {
1422 case Single: return u.single->apply (APPLY_ARG);
1423 case Pair: return u.pair->apply (APPLY_ARG);
1424 case Cursive: return u.cursive->apply (APPLY_ARG);
1425 case MarkBase: return u.markBase->apply (APPLY_ARG);
1426 case MarkLig: return u.markLig->apply (APPLY_ARG);
1427 case MarkMark: return u.markMark->apply (APPLY_ARG);
1428 case Context: return u.context->apply (APPLY_ARG);
1429 case ChainContext: return u.chainContext->apply (APPLY_ARG);
1430 case Extension: return u.extension->apply (APPLY_ARG);
1431 default:return false;
1435 inline bool sanitize (SANITIZE_ARG_DEF) {
1437 if (!SANITIZE (u.format)) return false;
1439 case Single: return u.single->sanitize (SANITIZE_ARG);
1440 case Pair: return u.pair->sanitize (SANITIZE_ARG);
1441 case Cursive: return u.cursive->sanitize (SANITIZE_ARG);
1442 case MarkBase: return u.markBase->sanitize (SANITIZE_ARG);
1443 case MarkLig: return u.markLig->sanitize (SANITIZE_ARG);
1444 case MarkMark: return u.markMark->sanitize (SANITIZE_ARG);
1445 case Context: return u.context->sanitize (SANITIZE_ARG);
1446 case ChainContext: return u.chainContext->sanitize (SANITIZE_ARG);
1447 case Extension: return u.extension->sanitize (SANITIZE_ARG);
1448 default:return true;
1455 SinglePos single[VAR];
1457 CursivePos cursive[VAR];
1458 MarkBasePos markBase[VAR];
1459 MarkLigPos markLig[VAR];
1460 MarkMarkPos markMark[VAR];
1461 ContextPos context[VAR];
1462 ChainContextPos chainContext[VAR];
1463 ExtensionPos extension[VAR];
1468 struct PosLookup : Lookup
1470 inline const PosLookupSubTable& get_subtable (unsigned int i) const
1471 { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1473 inline bool apply_once (hb_ot_layout_context_t *layout_context,
1474 hb_buffer_t *buffer,
1475 unsigned int context_length,
1476 unsigned int nesting_level_left,
1477 unsigned int apply_depth) const
1479 unsigned int lookup_type = get_type ();
1480 hb_apply_context_t context[1];
1482 context->nesting_level_left = nesting_level_left;
1483 context->lookup_flag = get_flag ();
1485 if (!_hb_ot_layout_check_glyph_property (layout_context->face, IN_CURINFO (), context->lookup_flag, &context->property))
1488 for (unsigned int i = 0; i < get_subtable_count (); i++)
1489 if (get_subtable (i).apply (APPLY_ARG, lookup_type))
1495 inline bool apply_string (hb_ot_layout_context_t *layout_context,
1496 hb_buffer_t *buffer,
1497 hb_mask_t mask) const
1501 if (unlikely (!buffer->in_length))
1504 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1507 while (buffer->in_pos < buffer->in_length)
1510 if (~IN_MASK (buffer->in_pos) & mask)
1512 done = apply_once (layout_context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL, 0);
1518 /* Contrary to properties defined in GDEF, user-defined properties
1519 will always stop a possible cursive positioning. */
1520 layout_context->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1530 inline bool sanitize (SANITIZE_ARG_DEF) {
1532 if (unlikely (!Lookup::sanitize (SANITIZE_ARG))) return false;
1533 OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1534 return SANITIZE_THIS (list);
1538 typedef OffsetListOf<PosLookup> PosLookupList;
1539 ASSERT_SIZE (PosLookupList, 2);
1545 struct GPOS : GSUBGPOS
1547 static const hb_tag_t Tag = HB_OT_TAG_GPOS;
1549 inline const PosLookup& get_lookup (unsigned int i) const
1550 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1552 inline bool position_lookup (hb_ot_layout_context_t *layout_context,
1553 hb_buffer_t *buffer,
1554 unsigned int lookup_index,
1555 hb_mask_t mask) const
1556 { return get_lookup (lookup_index).apply_string (layout_context, buffer, mask); }
1558 inline bool sanitize (SANITIZE_ARG_DEF) {
1560 if (unlikely (!GSUBGPOS::sanitize (SANITIZE_ARG))) return false;
1561 OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1562 return SANITIZE_THIS (list);
1565 ASSERT_SIZE (GPOS, 10);
1568 /* Out-of-class implementation for methods recursing */
1570 inline bool ExtensionPos::apply (APPLY_ARG_DEF) const
1573 return get_subtable ().apply (APPLY_ARG, get_type ());
1576 inline bool ExtensionPos::sanitize (SANITIZE_ARG_DEF)
1579 if (unlikely (!Extension::sanitize (SANITIZE_ARG))) return false;
1580 unsigned int offset = get_offset ();
1581 if (unlikely (!offset)) return true;
1582 return SANITIZE (StructAtOffset<PosLookupSubTable> (*this, offset));
1585 static inline bool position_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
1587 const GPOS &gpos = *(layout_context->face->ot_layout.gpos);
1588 const PosLookup &l = gpos.get_lookup (lookup_index);
1590 if (unlikely (context->nesting_level_left == 0))
1593 if (unlikely (context_length < 1))
1596 return l.apply_once (layout_context, buffer, context_length, context->nesting_level_left - 1, apply_depth + 1);
1600 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */