86 * writing) */
87 Offset xPlaDevice; /* Offset to Device table for
88 * horizontal placement--measured from
89 * beginning of PosTable (may be NULL) */
90 Offset yPlaDevice; /* Offset to Device table for vertical
91 * placement--measured from beginning
92 * of PosTable (may be NULL) */
93 Offset xAdvDevice; /* Offset to Device table for
94 * horizontal advance--measured from
95 * beginning of PosTable (may be NULL) */
96 Offset yAdvDevice; /* Offset to Device table for vertical
97 * advance--measured from beginning of
98 * PosTable (may be NULL) */
99 #endif
100
101 inline unsigned int get_len (void) const
102 { return _hb_popcount32 ((unsigned int) *this); }
103 inline unsigned int get_size (void) const
104 { return get_len () * Value::static_size; }
105
106 void apply_value (hb_font_t *font,
107 hb_direction_t direction,
108 const void *base,
109 const Value *values,
110 hb_glyph_position_t &glyph_pos) const
111 {
112 unsigned int x_ppem, y_ppem;
113 unsigned int format = *this;
114 hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (direction);
115
116 if (!format) return;
117
118 if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++));
119 if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++));
120 if (format & xAdvance) {
121 if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
122 values++;
123 }
124 /* y_advance values grow downward but font-space grows upward, hence negation */
125 if (format & yAdvance) {
126 if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
127 values++;
128 }
129
130 if (!has_device ()) return;
131
132 x_ppem = font->x_ppem;
133 y_ppem = font->y_ppem;
134
135 if (!x_ppem && !y_ppem) return;
136
137 /* pixel -> fractional pixel */
138 if (format & xPlaDevice) {
139 if (x_ppem) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font);
140 values++;
141 }
142 if (format & yPlaDevice) {
143 if (y_ppem) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font);
144 values++;
145 }
146 if (format & xAdvDevice) {
147 if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font);
148 values++;
149 }
150 if (format & yAdvDevice) {
151 /* y_advance values grow downward but font-space grows upward, hence negation */
152 if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font);
153 values++;
154 }
155 }
156
157 private:
158 inline bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
159 {
160 unsigned int format = *this;
161
162 if (format & xPlacement) values++;
163 if (format & yPlacement) values++;
164 if (format & xAdvance) values++;
165 if (format & yAdvance) values++;
166
167 if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
168 if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
169 if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
170 if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
171
172 return true;
214 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
215 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
216 {
217 TRACE_SANITIZE (this);
218
219 if (!has_device ()) return_trace (true);
220
221 for (unsigned int i = 0; i < count; i++) {
222 if (!sanitize_value_devices (c, base, values))
223 return_trace (false);
224 values += stride;
225 }
226
227 return_trace (true);
228 }
229 };
230
231
232 struct AnchorFormat1
233 {
234 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
235 hb_position_t *x, hb_position_t *y) const
236 {
237 *x = font->em_scale_x (xCoordinate);
238 *y = font->em_scale_y (yCoordinate);
239 }
240
241 inline bool sanitize (hb_sanitize_context_t *c) const
242 {
243 TRACE_SANITIZE (this);
244 return_trace (c->check_struct (this));
245 }
246
247 protected:
248 USHORT format; /* Format identifier--format = 1 */
249 SHORT xCoordinate; /* Horizontal value--in design units */
250 SHORT yCoordinate; /* Vertical value--in design units */
251 public:
252 DEFINE_SIZE_STATIC (6);
253 };
254
255 struct AnchorFormat2
256 {
257 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
258 hb_position_t *x, hb_position_t *y) const
259 {
260 unsigned int x_ppem = font->x_ppem;
261 unsigned int y_ppem = font->y_ppem;
262 hb_position_t cx, cy;
263 hb_bool_t ret;
264
265 ret = (x_ppem || y_ppem) &&
266 font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
267 *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
268 *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
269 }
270
271 inline bool sanitize (hb_sanitize_context_t *c) const
272 {
273 TRACE_SANITIZE (this);
274 return_trace (c->check_struct (this));
275 }
276
277 protected:
278 USHORT format; /* Format identifier--format = 2 */
279 SHORT xCoordinate; /* Horizontal value--in design units */
280 SHORT yCoordinate; /* Vertical value--in design units */
281 USHORT anchorPoint; /* Index to glyph contour point */
282 public:
283 DEFINE_SIZE_STATIC (8);
284 };
285
286 struct AnchorFormat3
287 {
288 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
289 hb_position_t *x, hb_position_t *y) const
290 {
291 *x = font->em_scale_x (xCoordinate);
292 *y = font->em_scale_y (yCoordinate);
293
294 if (font->x_ppem)
295 *x += (this+xDeviceTable).get_x_delta (font);
296 if (font->y_ppem)
297 *y += (this+yDeviceTable).get_x_delta (font);
298 }
299
300 inline bool sanitize (hb_sanitize_context_t *c) const
301 {
302 TRACE_SANITIZE (this);
303 return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
304 }
305
306 protected:
307 USHORT format; /* Format identifier--format = 3 */
308 SHORT xCoordinate; /* Horizontal value--in design units */
309 SHORT yCoordinate; /* Vertical value--in design units */
310 OffsetTo<Device>
311 xDeviceTable; /* Offset to Device table for X
312 * coordinate-- from beginning of
313 * Anchor table (may be NULL) */
314 OffsetTo<Device>
315 yDeviceTable; /* Offset to Device table for Y
316 * coordinate-- from beginning of
317 * Anchor table (may be NULL) */
318 public:
319 DEFINE_SIZE_STATIC (10);
320 };
321
322 struct Anchor
323 {
324 inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
325 hb_position_t *x, hb_position_t *y) const
326 {
327 *x = *y = 0;
328 switch (u.format) {
329 case 1: u.format1.get_anchor (font, glyph_id, x, y); return;
330 case 2: u.format2.get_anchor (font, glyph_id, x, y); return;
331 case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
332 default: return;
333 }
334 }
335
336 inline bool sanitize (hb_sanitize_context_t *c) const
337 {
338 TRACE_SANITIZE (this);
339 if (!u.format.sanitize (c)) return_trace (false);
340 switch (u.format) {
341 case 1: return_trace (u.format1.sanitize (c));
342 case 2: return_trace (u.format2.sanitize (c));
343 case 3: return_trace (u.format3.sanitize (c));
344 default:return_trace (true);
345 }
346 }
347
348 protected:
349 union {
350 USHORT format; /* Format identifier */
351 AnchorFormat1 format1;
353 AnchorFormat3 format3;
354 } u;
355 public:
356 DEFINE_SIZE_UNION (2, format);
357 };
358
359
360 struct AnchorMatrix
361 {
362 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
363 *found = false;
364 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
365 *found = !matrixZ[row * cols + col].is_null ();
366 return this+matrixZ[row * cols + col];
367 }
368
369 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
370 {
371 TRACE_SANITIZE (this);
372 if (!c->check_struct (this)) return_trace (false);
373 if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return_trace (false);
374 unsigned int count = rows * cols;
375 if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
376 for (unsigned int i = 0; i < count; i++)
377 if (!matrixZ[i].sanitize (c, this)) return_trace (false);
378 return_trace (true);
379 }
380
381 USHORT rows; /* Number of rows */
382 protected:
383 OffsetTo<Anchor>
384 matrixZ[VAR]; /* Matrix of offsets to Anchor tables--
385 * from beginning of AnchorMatrix table */
386 public:
387 DEFINE_SIZE_ARRAY (2, matrixZ);
388 };
389
390
391 struct MarkRecord
392 {
393 friend struct MarkArray;
411 {
412 inline bool apply (hb_apply_context_t *c,
413 unsigned int mark_index, unsigned int glyph_index,
414 const AnchorMatrix &anchors, unsigned int class_count,
415 unsigned int glyph_pos) const
416 {
417 TRACE_APPLY (this);
418 hb_buffer_t *buffer = c->buffer;
419 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
420 unsigned int mark_class = record.klass;
421
422 const Anchor& mark_anchor = this + record.markAnchor;
423 bool found;
424 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
425 /* If this subtable doesn't have an anchor for this base and this class,
426 * return false such that the subsequent subtables have a chance at it. */
427 if (unlikely (!found)) return_trace (false);
428
429 hb_position_t mark_x, mark_y, base_x, base_y;
430
431 mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y);
432 glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
433
434 hb_glyph_position_t &o = buffer->cur_pos();
435 o.x_offset = base_x - mark_x;
436 o.y_offset = base_y - mark_y;
437 o.attach_type() = ATTACH_TYPE_MARK;
438 o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
439 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
440
441 buffer->idx++;
442 return_trace (true);
443 }
444
445 inline bool sanitize (hb_sanitize_context_t *c) const
446 {
447 TRACE_SANITIZE (this);
448 return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
449 }
450 };
451
452
455 struct SinglePosFormat1
456 {
457 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
458 {
459 TRACE_COLLECT_GLYPHS (this);
460 (this+coverage).add_coverage (c->input);
461 }
462
463 inline const Coverage &get_coverage (void) const
464 {
465 return this+coverage;
466 }
467
468 inline bool apply (hb_apply_context_t *c) const
469 {
470 TRACE_APPLY (this);
471 hb_buffer_t *buffer = c->buffer;
472 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
473 if (likely (index == NOT_COVERED)) return_trace (false);
474
475 valueFormat.apply_value (c->font, c->direction, this,
476 values, buffer->cur_pos());
477
478 buffer->idx++;
479 return_trace (true);
480 }
481
482 inline bool sanitize (hb_sanitize_context_t *c) const
483 {
484 TRACE_SANITIZE (this);
485 return_trace (c->check_struct (this) &&
486 coverage.sanitize (c, this) &&
487 valueFormat.sanitize_value (c, this, values));
488 }
489
490 protected:
491 USHORT format; /* Format identifier--format = 1 */
492 OffsetTo<Coverage>
493 coverage; /* Offset to Coverage table--from
494 * beginning of subtable */
495 ValueFormat valueFormat; /* Defines the types of data in the
496 * ValueRecord */
506 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
507 {
508 TRACE_COLLECT_GLYPHS (this);
509 (this+coverage).add_coverage (c->input);
510 }
511
512 inline const Coverage &get_coverage (void) const
513 {
514 return this+coverage;
515 }
516
517 inline bool apply (hb_apply_context_t *c) const
518 {
519 TRACE_APPLY (this);
520 hb_buffer_t *buffer = c->buffer;
521 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
522 if (likely (index == NOT_COVERED)) return_trace (false);
523
524 if (likely (index >= valueCount)) return_trace (false);
525
526 valueFormat.apply_value (c->font, c->direction, this,
527 &values[index * valueFormat.get_len ()],
528 buffer->cur_pos());
529
530 buffer->idx++;
531 return_trace (true);
532 }
533
534 inline bool sanitize (hb_sanitize_context_t *c) const
535 {
536 TRACE_SANITIZE (this);
537 return_trace (c->check_struct (this) &&
538 coverage.sanitize (c, this) &&
539 valueFormat.sanitize_values (c, this, values, valueCount));
540 }
541
542 protected:
543 USHORT format; /* Format identifier--format = 2 */
544 OffsetTo<Coverage>
545 coverage; /* Offset to Coverage table--from
546 * beginning of subtable */
623
624 const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
625 unsigned int count = len;
626
627 /* Hand-coded bsearch. */
628 if (unlikely (!count))
629 return_trace (false);
630 hb_codepoint_t x = buffer->info[pos].codepoint;
631 int min = 0, max = (int) count - 1;
632 while (min <= max)
633 {
634 int mid = (min + max) / 2;
635 const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
636 hb_codepoint_t mid_x = record->secondGlyph;
637 if (x < mid_x)
638 max = mid - 1;
639 else if (x > mid_x)
640 min = mid + 1;
641 else
642 {
643 valueFormats[0].apply_value (c->font, c->direction, this,
644 &record->values[0], buffer->cur_pos());
645 valueFormats[1].apply_value (c->font, c->direction, this,
646 &record->values[len1], buffer->pos[pos]);
647 if (len2)
648 pos++;
649 buffer->idx = pos;
650 return_trace (true);
651 }
652 }
653
654 return_trace (false);
655 }
656
657 struct sanitize_closure_t {
658 const void *base;
659 const ValueFormat *valueFormats;
660 unsigned int len1; /* valueFormats[0].get_len() */
661 unsigned int stride; /* 1 + len1 + len2 */
662 };
663
664 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
665 {
666 TRACE_SANITIZE (this);
672 return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) &&
673 closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
674 }
675
676 protected:
677 USHORT len; /* Number of PairValueRecords */
678 USHORT arrayZ[VAR]; /* Array of PairValueRecords--ordered
679 * by GlyphID of the second glyph */
680 public:
681 DEFINE_SIZE_ARRAY (2, arrayZ);
682 };
683
684 struct PairPosFormat1
685 {
686 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
687 {
688 TRACE_COLLECT_GLYPHS (this);
689 (this+coverage).add_coverage (c->input);
690 unsigned int count = pairSet.len;
691 for (unsigned int i = 0; i < count; i++)
692 (this+pairSet[i]).collect_glyphs (c, &valueFormat1);
693 }
694
695 inline const Coverage &get_coverage (void) const
696 {
697 return this+coverage;
698 }
699
700 inline bool apply (hb_apply_context_t *c) const
701 {
702 TRACE_APPLY (this);
703 hb_buffer_t *buffer = c->buffer;
704 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
705 if (likely (index == NOT_COVERED)) return_trace (false);
706
707 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
708 skippy_iter.reset (buffer->idx, 1);
709 if (!skippy_iter.next ()) return_trace (false);
710
711 return_trace ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
712 }
713
714 inline bool sanitize (hb_sanitize_context_t *c) const
715 {
716 TRACE_SANITIZE (this);
717
718 if (!c->check_struct (this)) return_trace (false);
719
720 unsigned int len1 = valueFormat1.get_len ();
721 unsigned int len2 = valueFormat2.get_len ();
722 PairSet::sanitize_closure_t closure = {
723 this,
724 &valueFormat1,
725 len1,
726 1 + len1 + len2
727 };
728
729 return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
730 }
731
732 protected:
733 USHORT format; /* Format identifier--format = 1 */
734 OffsetTo<Coverage>
735 coverage; /* Offset to Coverage table--from
736 * beginning of subtable */
737 ValueFormat valueFormat1; /* Defines the types of data in
738 * ValueRecord1--for the first glyph
739 * in the pair--may be zero (0) */
740 ValueFormat valueFormat2; /* Defines the types of data in
741 * ValueRecord2--for the second glyph
742 * in the pair--may be zero (0) */
743 OffsetArrayOf<PairSet>
744 pairSet; /* Array of PairSet tables
745 * ordered by Coverage Index */
746 public:
747 DEFINE_SIZE_ARRAY (10, pairSet);
748 };
749
750 struct PairPosFormat2
751 {
752 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
753 {
754 TRACE_COLLECT_GLYPHS (this);
755 (this+coverage).add_coverage (c->input);
756
757 unsigned int count1 = class1Count;
758 const ClassDef &klass1 = this+classDef1;
759 for (unsigned int i = 0; i < count1; i++)
760 klass1.add_class (c->input, i);
773 inline bool apply (hb_apply_context_t *c) const
774 {
775 TRACE_APPLY (this);
776 hb_buffer_t *buffer = c->buffer;
777 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
778 if (likely (index == NOT_COVERED)) return_trace (false);
779
780 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
781 skippy_iter.reset (buffer->idx, 1);
782 if (!skippy_iter.next ()) return_trace (false);
783
784 unsigned int len1 = valueFormat1.get_len ();
785 unsigned int len2 = valueFormat2.get_len ();
786 unsigned int record_len = len1 + len2;
787
788 unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
789 unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
790 if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
791
792 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
793 valueFormat1.apply_value (c->font, c->direction, this,
794 v, buffer->cur_pos());
795 valueFormat2.apply_value (c->font, c->direction, this,
796 v + len1, buffer->pos[skippy_iter.idx]);
797
798 buffer->idx = skippy_iter.idx;
799 if (len2)
800 buffer->idx++;
801
802 return_trace (true);
803 }
804
805 inline bool sanitize (hb_sanitize_context_t *c) const
806 {
807 TRACE_SANITIZE (this);
808 if (!(c->check_struct (this)
809 && coverage.sanitize (c, this)
810 && classDef1.sanitize (c, this)
811 && classDef2.sanitize (c, this))) return_trace (false);
812
813 unsigned int len1 = valueFormat1.get_len ();
814 unsigned int len2 = valueFormat2.get_len ();
815 unsigned int stride = len1 + len2;
816 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
914
915 inline bool apply (hb_apply_context_t *c) const
916 {
917 TRACE_APPLY (this);
918 hb_buffer_t *buffer = c->buffer;
919
920 const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)];
921 if (!this_record.exitAnchor) return_trace (false);
922
923 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
924 skippy_iter.reset (buffer->idx, 1);
925 if (!skippy_iter.next ()) return_trace (false);
926
927 const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
928 if (!next_record.entryAnchor) return_trace (false);
929
930 unsigned int i = buffer->idx;
931 unsigned int j = skippy_iter.idx;
932
933 hb_position_t entry_x, entry_y, exit_x, exit_y;
934 (this+this_record.exitAnchor).get_anchor (c->font, buffer->info[i].codepoint, &exit_x, &exit_y);
935 (this+next_record.entryAnchor).get_anchor (c->font, buffer->info[j].codepoint, &entry_x, &entry_y);
936
937 hb_glyph_position_t *pos = buffer->pos;
938
939 hb_position_t d;
940 /* Main-direction adjustment */
941 switch (c->direction) {
942 case HB_DIRECTION_LTR:
943 pos[i].x_advance = exit_x + pos[i].x_offset;
944
945 d = entry_x + pos[j].x_offset;
946 pos[j].x_advance -= d;
947 pos[j].x_offset -= d;
948 break;
949 case HB_DIRECTION_RTL:
950 d = exit_x + pos[i].x_offset;
951 pos[i].x_advance -= d;
952 pos[i].x_offset -= d;
953
954 pos[j].x_advance = entry_x + pos[j].x_offset;
955 break;
1502 */
1503
1504 struct GPOS : GSUBGPOS
1505 {
1506 static const hb_tag_t tableTag = HB_OT_TAG_GPOS;
1507
1508 inline const PosLookup& get_lookup (unsigned int i) const
1509 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1510
1511 static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
1512 static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
1513 static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
1514
1515 inline bool sanitize (hb_sanitize_context_t *c) const
1516 {
1517 TRACE_SANITIZE (this);
1518 if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
1519 const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1520 return_trace (list.sanitize (c, this));
1521 }
1522 public:
1523 DEFINE_SIZE_STATIC (10);
1524 };
1525
1526
1527 static void
1528 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
1529 {
1530 int chain = pos[i].attach_chain(), type = pos[i].attach_type();
1531 if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
1532 return;
1533
1534 pos[i].attach_chain() = 0;
1535
1536 unsigned int j = (int) i + chain;
1537
1538 /* Stop if we see new parent in the chain. */
1539 if (j == new_parent)
1540 return;
1541
1542 reverse_cursive_minor_offset (pos, j, direction, new_parent);
1543
|
86 * writing) */
87 Offset xPlaDevice; /* Offset to Device table for
88 * horizontal placement--measured from
89 * beginning of PosTable (may be NULL) */
90 Offset yPlaDevice; /* Offset to Device table for vertical
91 * placement--measured from beginning
92 * of PosTable (may be NULL) */
93 Offset xAdvDevice; /* Offset to Device table for
94 * horizontal advance--measured from
95 * beginning of PosTable (may be NULL) */
96 Offset yAdvDevice; /* Offset to Device table for vertical
97 * advance--measured from beginning of
98 * PosTable (may be NULL) */
99 #endif
100
101 inline unsigned int get_len (void) const
102 { return _hb_popcount32 ((unsigned int) *this); }
103 inline unsigned int get_size (void) const
104 { return get_len () * Value::static_size; }
105
106 void apply_value (hb_apply_context_t *c,
107 const void *base,
108 const Value *values,
109 hb_glyph_position_t &glyph_pos) const
110 {
111 unsigned int format = *this;
112 if (!format) return;
113
114 hb_font_t *font = c->font;
115 hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
116
117 if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++));
118 if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++));
119 if (format & xAdvance) {
120 if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
121 values++;
122 }
123 /* y_advance values grow downward but font-space grows upward, hence negation */
124 if (format & yAdvance) {
125 if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
126 values++;
127 }
128
129 if (!has_device ()) return;
130
131 bool use_x_device = font->x_ppem || font->num_coords;
132 bool use_y_device = font->y_ppem || font->num_coords;
133
134 if (!use_x_device && !use_y_device) return;
135
136 const VariationStore &store = c->var_store;
137
138 /* pixel -> fractional pixel */
139 if (format & xPlaDevice) {
140 if (use_x_device) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font, store);
141 values++;
142 }
143 if (format & yPlaDevice) {
144 if (use_y_device) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font, store);
145 values++;
146 }
147 if (format & xAdvDevice) {
148 if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font, store);
149 values++;
150 }
151 if (format & yAdvDevice) {
152 /* y_advance values grow downward but font-space grows upward, hence negation */
153 if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font, store);
154 values++;
155 }
156 }
157
158 private:
159 inline bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
160 {
161 unsigned int format = *this;
162
163 if (format & xPlacement) values++;
164 if (format & yPlacement) values++;
165 if (format & xAdvance) values++;
166 if (format & yAdvance) values++;
167
168 if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
169 if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
170 if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
171 if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
172
173 return true;
215 /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
216 inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
217 {
218 TRACE_SANITIZE (this);
219
220 if (!has_device ()) return_trace (true);
221
222 for (unsigned int i = 0; i < count; i++) {
223 if (!sanitize_value_devices (c, base, values))
224 return_trace (false);
225 values += stride;
226 }
227
228 return_trace (true);
229 }
230 };
231
232
233 struct AnchorFormat1
234 {
235 inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
236 hb_position_t *x, hb_position_t *y) const
237 {
238 hb_font_t *font = c->font;
239 *x = font->em_scale_x (xCoordinate);
240 *y = font->em_scale_y (yCoordinate);
241 }
242
243 inline bool sanitize (hb_sanitize_context_t *c) const
244 {
245 TRACE_SANITIZE (this);
246 return_trace (c->check_struct (this));
247 }
248
249 protected:
250 USHORT format; /* Format identifier--format = 1 */
251 SHORT xCoordinate; /* Horizontal value--in design units */
252 SHORT yCoordinate; /* Vertical value--in design units */
253 public:
254 DEFINE_SIZE_STATIC (6);
255 };
256
257 struct AnchorFormat2
258 {
259 inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
260 hb_position_t *x, hb_position_t *y) const
261 {
262 hb_font_t *font = c->font;
263 unsigned int x_ppem = font->x_ppem;
264 unsigned int y_ppem = font->y_ppem;
265 hb_position_t cx, cy;
266 hb_bool_t ret;
267
268 ret = (x_ppem || y_ppem) &&
269 font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
270 *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
271 *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
272 }
273
274 inline bool sanitize (hb_sanitize_context_t *c) const
275 {
276 TRACE_SANITIZE (this);
277 return_trace (c->check_struct (this));
278 }
279
280 protected:
281 USHORT format; /* Format identifier--format = 2 */
282 SHORT xCoordinate; /* Horizontal value--in design units */
283 SHORT yCoordinate; /* Vertical value--in design units */
284 USHORT anchorPoint; /* Index to glyph contour point */
285 public:
286 DEFINE_SIZE_STATIC (8);
287 };
288
289 struct AnchorFormat3
290 {
291 inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
292 hb_position_t *x, hb_position_t *y) const
293 {
294 hb_font_t *font = c->font;
295 *x = font->em_scale_x (xCoordinate);
296 *y = font->em_scale_y (yCoordinate);
297
298 if (font->x_ppem || font->num_coords)
299 *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
300 if (font->y_ppem || font->num_coords)
301 *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
302 }
303
304 inline bool sanitize (hb_sanitize_context_t *c) const
305 {
306 TRACE_SANITIZE (this);
307 return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
308 }
309
310 protected:
311 USHORT format; /* Format identifier--format = 3 */
312 SHORT xCoordinate; /* Horizontal value--in design units */
313 SHORT yCoordinate; /* Vertical value--in design units */
314 OffsetTo<Device>
315 xDeviceTable; /* Offset to Device table for X
316 * coordinate-- from beginning of
317 * Anchor table (may be NULL) */
318 OffsetTo<Device>
319 yDeviceTable; /* Offset to Device table for Y
320 * coordinate-- from beginning of
321 * Anchor table (may be NULL) */
322 public:
323 DEFINE_SIZE_STATIC (10);
324 };
325
326 struct Anchor
327 {
328 inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
329 hb_position_t *x, hb_position_t *y) const
330 {
331 *x = *y = 0;
332 switch (u.format) {
333 case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
334 case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
335 case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
336 default: return;
337 }
338 }
339
340 inline bool sanitize (hb_sanitize_context_t *c) const
341 {
342 TRACE_SANITIZE (this);
343 if (!u.format.sanitize (c)) return_trace (false);
344 switch (u.format) {
345 case 1: return_trace (u.format1.sanitize (c));
346 case 2: return_trace (u.format2.sanitize (c));
347 case 3: return_trace (u.format3.sanitize (c));
348 default:return_trace (true);
349 }
350 }
351
352 protected:
353 union {
354 USHORT format; /* Format identifier */
355 AnchorFormat1 format1;
357 AnchorFormat3 format3;
358 } u;
359 public:
360 DEFINE_SIZE_UNION (2, format);
361 };
362
363
364 struct AnchorMatrix
365 {
366 inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
367 *found = false;
368 if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
369 *found = !matrixZ[row * cols + col].is_null ();
370 return this+matrixZ[row * cols + col];
371 }
372
373 inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
374 {
375 TRACE_SANITIZE (this);
376 if (!c->check_struct (this)) return_trace (false);
377 if (unlikely (_hb_unsigned_int_mul_overflows (rows, cols))) return_trace (false);
378 unsigned int count = rows * cols;
379 if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
380 for (unsigned int i = 0; i < count; i++)
381 if (!matrixZ[i].sanitize (c, this)) return_trace (false);
382 return_trace (true);
383 }
384
385 USHORT rows; /* Number of rows */
386 protected:
387 OffsetTo<Anchor>
388 matrixZ[VAR]; /* Matrix of offsets to Anchor tables--
389 * from beginning of AnchorMatrix table */
390 public:
391 DEFINE_SIZE_ARRAY (2, matrixZ);
392 };
393
394
395 struct MarkRecord
396 {
397 friend struct MarkArray;
415 {
416 inline bool apply (hb_apply_context_t *c,
417 unsigned int mark_index, unsigned int glyph_index,
418 const AnchorMatrix &anchors, unsigned int class_count,
419 unsigned int glyph_pos) const
420 {
421 TRACE_APPLY (this);
422 hb_buffer_t *buffer = c->buffer;
423 const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
424 unsigned int mark_class = record.klass;
425
426 const Anchor& mark_anchor = this + record.markAnchor;
427 bool found;
428 const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
429 /* If this subtable doesn't have an anchor for this base and this class,
430 * return false such that the subsequent subtables have a chance at it. */
431 if (unlikely (!found)) return_trace (false);
432
433 hb_position_t mark_x, mark_y, base_x, base_y;
434
435 mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
436 glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
437
438 hb_glyph_position_t &o = buffer->cur_pos();
439 o.x_offset = base_x - mark_x;
440 o.y_offset = base_y - mark_y;
441 o.attach_type() = ATTACH_TYPE_MARK;
442 o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
443 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
444
445 buffer->idx++;
446 return_trace (true);
447 }
448
449 inline bool sanitize (hb_sanitize_context_t *c) const
450 {
451 TRACE_SANITIZE (this);
452 return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
453 }
454 };
455
456
459 struct SinglePosFormat1
460 {
461 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
462 {
463 TRACE_COLLECT_GLYPHS (this);
464 (this+coverage).add_coverage (c->input);
465 }
466
467 inline const Coverage &get_coverage (void) const
468 {
469 return this+coverage;
470 }
471
472 inline bool apply (hb_apply_context_t *c) const
473 {
474 TRACE_APPLY (this);
475 hb_buffer_t *buffer = c->buffer;
476 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
477 if (likely (index == NOT_COVERED)) return_trace (false);
478
479 valueFormat.apply_value (c, this, values, buffer->cur_pos());
480
481 buffer->idx++;
482 return_trace (true);
483 }
484
485 inline bool sanitize (hb_sanitize_context_t *c) const
486 {
487 TRACE_SANITIZE (this);
488 return_trace (c->check_struct (this) &&
489 coverage.sanitize (c, this) &&
490 valueFormat.sanitize_value (c, this, values));
491 }
492
493 protected:
494 USHORT format; /* Format identifier--format = 1 */
495 OffsetTo<Coverage>
496 coverage; /* Offset to Coverage table--from
497 * beginning of subtable */
498 ValueFormat valueFormat; /* Defines the types of data in the
499 * ValueRecord */
509 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
510 {
511 TRACE_COLLECT_GLYPHS (this);
512 (this+coverage).add_coverage (c->input);
513 }
514
515 inline const Coverage &get_coverage (void) const
516 {
517 return this+coverage;
518 }
519
520 inline bool apply (hb_apply_context_t *c) const
521 {
522 TRACE_APPLY (this);
523 hb_buffer_t *buffer = c->buffer;
524 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
525 if (likely (index == NOT_COVERED)) return_trace (false);
526
527 if (likely (index >= valueCount)) return_trace (false);
528
529 valueFormat.apply_value (c, this,
530 &values[index * valueFormat.get_len ()],
531 buffer->cur_pos());
532
533 buffer->idx++;
534 return_trace (true);
535 }
536
537 inline bool sanitize (hb_sanitize_context_t *c) const
538 {
539 TRACE_SANITIZE (this);
540 return_trace (c->check_struct (this) &&
541 coverage.sanitize (c, this) &&
542 valueFormat.sanitize_values (c, this, values, valueCount));
543 }
544
545 protected:
546 USHORT format; /* Format identifier--format = 2 */
547 OffsetTo<Coverage>
548 coverage; /* Offset to Coverage table--from
549 * beginning of subtable */
626
627 const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
628 unsigned int count = len;
629
630 /* Hand-coded bsearch. */
631 if (unlikely (!count))
632 return_trace (false);
633 hb_codepoint_t x = buffer->info[pos].codepoint;
634 int min = 0, max = (int) count - 1;
635 while (min <= max)
636 {
637 int mid = (min + max) / 2;
638 const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
639 hb_codepoint_t mid_x = record->secondGlyph;
640 if (x < mid_x)
641 max = mid - 1;
642 else if (x > mid_x)
643 min = mid + 1;
644 else
645 {
646 valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
647 valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
648 if (len2)
649 pos++;
650 buffer->idx = pos;
651 return_trace (true);
652 }
653 }
654
655 return_trace (false);
656 }
657
658 struct sanitize_closure_t {
659 const void *base;
660 const ValueFormat *valueFormats;
661 unsigned int len1; /* valueFormats[0].get_len() */
662 unsigned int stride; /* 1 + len1 + len2 */
663 };
664
665 inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
666 {
667 TRACE_SANITIZE (this);
673 return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) &&
674 closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
675 }
676
677 protected:
678 USHORT len; /* Number of PairValueRecords */
679 USHORT arrayZ[VAR]; /* Array of PairValueRecords--ordered
680 * by GlyphID of the second glyph */
681 public:
682 DEFINE_SIZE_ARRAY (2, arrayZ);
683 };
684
685 struct PairPosFormat1
686 {
687 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
688 {
689 TRACE_COLLECT_GLYPHS (this);
690 (this+coverage).add_coverage (c->input);
691 unsigned int count = pairSet.len;
692 for (unsigned int i = 0; i < count; i++)
693 (this+pairSet[i]).collect_glyphs (c, valueFormat);
694 }
695
696 inline const Coverage &get_coverage (void) const
697 {
698 return this+coverage;
699 }
700
701 inline bool apply (hb_apply_context_t *c) const
702 {
703 TRACE_APPLY (this);
704 hb_buffer_t *buffer = c->buffer;
705 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
706 if (likely (index == NOT_COVERED)) return_trace (false);
707
708 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
709 skippy_iter.reset (buffer->idx, 1);
710 if (!skippy_iter.next ()) return_trace (false);
711
712 return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
713 }
714
715 inline bool sanitize (hb_sanitize_context_t *c) const
716 {
717 TRACE_SANITIZE (this);
718
719 if (!c->check_struct (this)) return_trace (false);
720
721 unsigned int len1 = valueFormat[0].get_len ();
722 unsigned int len2 = valueFormat[1].get_len ();
723 PairSet::sanitize_closure_t closure = {
724 this,
725 valueFormat,
726 len1,
727 1 + len1 + len2
728 };
729
730 return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
731 }
732
733 protected:
734 USHORT format; /* Format identifier--format = 1 */
735 OffsetTo<Coverage>
736 coverage; /* Offset to Coverage table--from
737 * beginning of subtable */
738 ValueFormat valueFormat[2]; /* [0] Defines the types of data in
739 * ValueRecord1--for the first glyph
740 * in the pair--may be zero (0) */
741 /* [1] Defines the types of data in
742 * ValueRecord2--for the second glyph
743 * in the pair--may be zero (0) */
744 OffsetArrayOf<PairSet>
745 pairSet; /* Array of PairSet tables
746 * ordered by Coverage Index */
747 public:
748 DEFINE_SIZE_ARRAY (10, pairSet);
749 };
750
751 struct PairPosFormat2
752 {
753 inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
754 {
755 TRACE_COLLECT_GLYPHS (this);
756 (this+coverage).add_coverage (c->input);
757
758 unsigned int count1 = class1Count;
759 const ClassDef &klass1 = this+classDef1;
760 for (unsigned int i = 0; i < count1; i++)
761 klass1.add_class (c->input, i);
774 inline bool apply (hb_apply_context_t *c) const
775 {
776 TRACE_APPLY (this);
777 hb_buffer_t *buffer = c->buffer;
778 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
779 if (likely (index == NOT_COVERED)) return_trace (false);
780
781 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
782 skippy_iter.reset (buffer->idx, 1);
783 if (!skippy_iter.next ()) return_trace (false);
784
785 unsigned int len1 = valueFormat1.get_len ();
786 unsigned int len2 = valueFormat2.get_len ();
787 unsigned int record_len = len1 + len2;
788
789 unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
790 unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
791 if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
792
793 const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
794 valueFormat1.apply_value (c, this, v, buffer->cur_pos());
795 valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
796
797 buffer->idx = skippy_iter.idx;
798 if (len2)
799 buffer->idx++;
800
801 return_trace (true);
802 }
803
804 inline bool sanitize (hb_sanitize_context_t *c) const
805 {
806 TRACE_SANITIZE (this);
807 if (!(c->check_struct (this)
808 && coverage.sanitize (c, this)
809 && classDef1.sanitize (c, this)
810 && classDef2.sanitize (c, this))) return_trace (false);
811
812 unsigned int len1 = valueFormat1.get_len ();
813 unsigned int len2 = valueFormat2.get_len ();
814 unsigned int stride = len1 + len2;
815 unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
913
914 inline bool apply (hb_apply_context_t *c) const
915 {
916 TRACE_APPLY (this);
917 hb_buffer_t *buffer = c->buffer;
918
919 const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)];
920 if (!this_record.exitAnchor) return_trace (false);
921
922 hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
923 skippy_iter.reset (buffer->idx, 1);
924 if (!skippy_iter.next ()) return_trace (false);
925
926 const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
927 if (!next_record.entryAnchor) return_trace (false);
928
929 unsigned int i = buffer->idx;
930 unsigned int j = skippy_iter.idx;
931
932 hb_position_t entry_x, entry_y, exit_x, exit_y;
933 (this+this_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
934 (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
935
936 hb_glyph_position_t *pos = buffer->pos;
937
938 hb_position_t d;
939 /* Main-direction adjustment */
940 switch (c->direction) {
941 case HB_DIRECTION_LTR:
942 pos[i].x_advance = exit_x + pos[i].x_offset;
943
944 d = entry_x + pos[j].x_offset;
945 pos[j].x_advance -= d;
946 pos[j].x_offset -= d;
947 break;
948 case HB_DIRECTION_RTL:
949 d = exit_x + pos[i].x_offset;
950 pos[i].x_advance -= d;
951 pos[i].x_offset -= d;
952
953 pos[j].x_advance = entry_x + pos[j].x_offset;
954 break;
1501 */
1502
1503 struct GPOS : GSUBGPOS
1504 {
1505 static const hb_tag_t tableTag = HB_OT_TAG_GPOS;
1506
1507 inline const PosLookup& get_lookup (unsigned int i) const
1508 { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1509
1510 static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
1511 static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
1512 static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
1513
1514 inline bool sanitize (hb_sanitize_context_t *c) const
1515 {
1516 TRACE_SANITIZE (this);
1517 if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
1518 const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1519 return_trace (list.sanitize (c, this));
1520 }
1521 };
1522
1523
1524 static void
1525 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
1526 {
1527 int chain = pos[i].attach_chain(), type = pos[i].attach_type();
1528 if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
1529 return;
1530
1531 pos[i].attach_chain() = 0;
1532
1533 unsigned int j = (int) i + chain;
1534
1535 /* Stop if we see new parent in the chain. */
1536 if (j == new_parent)
1537 return;
1538
1539 reverse_cursive_minor_offset (pos, j, direction, new_parent);
1540
|