GPOS.hh (5946B)
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(); 84 int type = pos[i].attach_type(); 85 86 pos[i].attach_chain() = 0; 87 88 unsigned int j = (int) i + chain; 89 90 if (unlikely (j >= len)) 91 return; 92 93 if (unlikely (!nesting_level)) 94 return; 95 96 if (pos[j].attach_chain()) 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 // i is the position of the mark; j is the base. 114 if (j < i) 115 { 116 /* This is the common case: mark follows base. 117 * And currently the only way in OpenType. */ 118 if (HB_DIRECTION_IS_FORWARD (direction)) 119 for (unsigned int k = j; k < i; k++) { 120 pos[i].x_offset -= pos[k].x_advance; 121 pos[i].y_offset -= pos[k].y_advance; 122 } 123 else 124 for (unsigned int k = j + 1; k < i + 1; k++) { 125 pos[i].x_offset += pos[k].x_advance; 126 pos[i].y_offset += pos[k].y_advance; 127 } 128 } 129 else // j > i 130 { 131 /* This can happen with `kerx`: a mark attaching 132 * to a base after it in the logical order. */ 133 if (HB_DIRECTION_IS_FORWARD (direction)) 134 for (unsigned int k = i; k < j; k++) { 135 pos[i].x_offset += pos[k].x_advance; 136 pos[i].y_offset += pos[k].y_advance; 137 } 138 else 139 for (unsigned int k = i + 1; k < j + 1; k++) { 140 pos[i].x_offset -= pos[k].x_advance; 141 pos[i].y_offset -= pos[k].y_advance; 142 } 143 } 144 } 145 } 146 147 void 148 GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) 149 { 150 unsigned int count = buffer->len; 151 for (unsigned int i = 0; i < count; i++) 152 buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; 153 } 154 155 void 156 GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) 157 { 158 //_hb_buffer_assert_gsubgpos_vars (buffer); 159 } 160 161 void 162 GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) 163 { 164 _hb_buffer_assert_gsubgpos_vars (buffer); 165 166 unsigned int len; 167 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); 168 hb_direction_t direction = buffer->props.direction; 169 170 /* Handle attachments */ 171 if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) 172 { 173 auto *pos = buffer->pos; 174 // https://github.com/harfbuzz/harfbuzz/issues/5514 175 if (HB_DIRECTION_IS_FORWARD (direction)) 176 { 177 for (unsigned i = 0; i < len; i++) 178 if (pos[i].attach_chain()) 179 propagate_attachment_offsets (pos, len, i, direction); 180 } else { 181 for (unsigned i = len; i-- > 0; ) 182 if (pos[i].attach_chain()) 183 propagate_attachment_offsets (pos, len, i, direction); 184 } 185 } 186 187 if (unlikely (font->slant_xy) && 188 HB_DIRECTION_IS_HORIZONTAL (direction)) 189 { 190 /* Slanting shaping results is only supported for horizontal text, 191 * as it gets weird otherwise. */ 192 for (unsigned i = 0; i < len; i++) 193 if (unlikely (pos[i].y_offset)) 194 pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset); 195 } 196 } 197 198 } 199 200 struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { 201 GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} 202 }; 203 204 } 205 206 #endif /* OT_LAYOUT_GPOS_GPOS_HH */