| 1 | #ifndef OT_LAYOUT_GPOS_GPOS_HH |
| 2 | #define OT_LAYOUT_GPOS_GPOS_HH |
| 3 | |
| 4 | #include "../../../hb-ot-layout-common.hh" |
| 5 | #include "../../../hb-ot-layout-gsubgpos.hh" |
| 6 | #include "Common.hh" |
| 7 | #include "PosLookup.hh" |
| 8 | |
| 9 | namespace OT { |
| 10 | |
| 11 | using Layout::GPOS_impl::PosLookup; |
| 12 | |
| 13 | namespace Layout { |
| 14 | |
| 15 | static void |
| 16 | propagate_attachment_offsets (hb_glyph_position_t *pos, |
| 17 | unsigned int len, |
| 18 | unsigned int i, |
| 19 | hb_direction_t direction, |
| 20 | unsigned nesting_level = HB_MAX_NESTING_LEVEL); |
| 21 | |
| 22 | /* |
| 23 | * GPOS -- Glyph Positioning |
| 24 | * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos |
| 25 | */ |
| 26 | |
| 27 | struct GPOS : GSUBGPOS |
| 28 | { |
| 29 | static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; |
| 30 | |
| 31 | using Lookup = PosLookup; |
| 32 | |
| 33 | const PosLookup& get_lookup (unsigned int i) const |
| 34 | { return static_cast<const PosLookup &> (GSUBGPOS::get_lookup (i)); } |
| 35 | |
| 36 | static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); |
| 37 | static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); |
| 38 | static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); |
| 39 | |
| 40 | bool subset (hb_subset_context_t *c) const |
| 41 | { |
| 42 | hb_subset_layout_context_t l (c, tableTag); |
| 43 | return GSUBGPOS::subset<PosLookup> (&l); |
| 44 | } |
| 45 | |
| 46 | bool sanitize (hb_sanitize_context_t *c) const |
| 47 | { |
| 48 | TRACE_SANITIZE (this); |
| 49 | return_trace (GSUBGPOS::sanitize<PosLookup> (c)); |
| 50 | } |
| 51 | |
| 52 | HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, |
| 53 | hb_face_t *face) const; |
| 54 | |
| 55 | void collect_variation_indices (hb_collect_variation_indices_context_t *c) const |
| 56 | { |
| 57 | for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) |
| 58 | { |
| 59 | if (!c->gpos_lookups->has (i)) continue; |
| 60 | const PosLookup &l = get_lookup (i); |
| 61 | l.dispatch (c); |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | void closure_lookups (hb_face_t *face, |
| 66 | const hb_set_t *glyphs, |
| 67 | hb_set_t *lookup_indexes /* IN/OUT */) const |
| 68 | { GSUBGPOS::closure_lookups<PosLookup> (face, glyphs, lookup_indexes); } |
| 69 | |
| 70 | typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t; |
| 71 | }; |
| 72 | |
| 73 | |
| 74 | static void |
| 75 | propagate_attachment_offsets (hb_glyph_position_t *pos, |
| 76 | unsigned int len, |
| 77 | unsigned int i, |
| 78 | hb_direction_t direction, |
| 79 | unsigned nesting_level) |
| 80 | { |
| 81 | /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate |
| 82 | * offset of glyph they are attached to. */ |
| 83 | int chain = pos[i].attach_chain(), type = pos[i].attach_type(); |
| 84 | if (likely (!chain)) |
| 85 | return; |
| 86 | |
| 87 | pos[i].attach_chain() = 0; |
| 88 | |
| 89 | unsigned int j = (int) i + chain; |
| 90 | |
| 91 | if (unlikely (j >= len)) |
| 92 | return; |
| 93 | |
| 94 | if (unlikely (!nesting_level)) |
| 95 | return; |
| 96 | |
| 97 | propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); |
| 98 | |
| 99 | assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); |
| 100 | |
| 101 | if (type & GPOS_impl::ATTACH_TYPE_CURSIVE) |
| 102 | { |
| 103 | if (HB_DIRECTION_IS_HORIZONTAL (direction)) |
| 104 | pos[i].y_offset += pos[j].y_offset; |
| 105 | else |
| 106 | pos[i].x_offset += pos[j].x_offset; |
| 107 | } |
| 108 | else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/ |
| 109 | { |
| 110 | pos[i].x_offset += pos[j].x_offset; |
| 111 | pos[i].y_offset += pos[j].y_offset; |
| 112 | |
| 113 | assert (j < i); |
| 114 | if (HB_DIRECTION_IS_FORWARD (direction)) |
| 115 | for (unsigned int k = j; k < i; k++) { |
| 116 | pos[i].x_offset -= pos[k].x_advance; |
| 117 | pos[i].y_offset -= pos[k].y_advance; |
| 118 | } |
| 119 | else |
| 120 | for (unsigned int k = j + 1; k < i + 1; k++) { |
| 121 | pos[i].x_offset += pos[k].x_advance; |
| 122 | pos[i].y_offset += pos[k].y_advance; |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void |
| 128 | GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) |
| 129 | { |
| 130 | unsigned int count = buffer->len; |
| 131 | for (unsigned int i = 0; i < count; i++) |
| 132 | buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; |
| 133 | } |
| 134 | |
| 135 | void |
| 136 | GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) |
| 137 | { |
| 138 | //_hb_buffer_assert_gsubgpos_vars (buffer); |
| 139 | } |
| 140 | |
| 141 | void |
| 142 | GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) |
| 143 | { |
| 144 | _hb_buffer_assert_gsubgpos_vars (buffer); |
| 145 | |
| 146 | unsigned int len; |
| 147 | hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); |
| 148 | hb_direction_t direction = buffer->props.direction; |
| 149 | |
| 150 | /* Handle attachments */ |
| 151 | if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) |
| 152 | for (unsigned i = 0; i < len; i++) |
| 153 | propagate_attachment_offsets (pos, len, i, direction); |
| 154 | |
| 155 | if (unlikely (font->slant)) |
| 156 | { |
| 157 | for (unsigned i = 0; i < len; i++) |
| 158 | if (unlikely (pos[i].y_offset)) |
| 159 | pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | |
| 165 | struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { |
| 166 | GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} |
| 167 | }; |
| 168 | |
| 169 | } |
| 170 | |
| 171 | #endif /* OT_LAYOUT_GPOS_GPOS_HH */ |
| 172 | |