1#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
2#define OT_LAYOUT_GPOS_PAIRSET_HH
3
4#include "PairValueRecord.hh"
5
6namespace OT {
7namespace Layout {
8namespace GPOS_impl {
9
10
11template <typename Types>
12struct PairSet
13{
14 template <typename Types2>
15 friend struct PairPosFormat1_3;
16
17 using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
18
19 protected:
20 HBUINT16 len; /* Number of PairValueRecords */
21 PairValueRecord firstPairValueRecord;
22 /* Array of PairValueRecords--ordered
23 * by GlyphID of the second glyph */
24 public:
25 DEFINE_SIZE_MIN (2);
26
27 static unsigned get_size (unsigned len1, unsigned len2)
28 {
29 return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2);
30 }
31 static unsigned get_size (const ValueFormat valueFormats[2])
32 {
33 unsigned len1 = valueFormats[0].get_len ();
34 unsigned len2 = valueFormats[1].get_len ();
35 return get_size (len1, len2);
36 }
37
38 struct sanitize_closure_t
39 {
40 const ValueFormat *valueFormats;
41 unsigned int len1; /* valueFormats[0].get_len() */
42 unsigned int stride; /* bytes */
43 };
44
45 bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
46 {
47 TRACE_SANITIZE (this);
48 if (!(c->check_struct (this)
49 && c->check_range (&firstPairValueRecord,
50 len,
51 closure->stride))) return_trace (false);
52
53 unsigned int count = len;
54 const PairValueRecord *record = &firstPairValueRecord;
55 return_trace (c->lazy_some_gpos ||
56 (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
57 closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)));
58 }
59
60 bool intersects (const hb_set_t *glyphs,
61 const ValueFormat *valueFormats) const
62 {
63 unsigned record_size = get_size (valueFormats);
64
65 const PairValueRecord *record = &firstPairValueRecord;
66 unsigned int count = len;
67 for (unsigned int i = 0; i < count; i++)
68 {
69 if (glyphs->has (record->secondGlyph))
70 return true;
71 record = &StructAtOffset<const PairValueRecord> (record, record_size);
72 }
73 return false;
74 }
75
76 void collect_glyphs (hb_collect_glyphs_context_t *c,
77 const ValueFormat *valueFormats) const
78 {
79 unsigned record_size = get_size (valueFormats);
80
81 const PairValueRecord *record = &firstPairValueRecord;
82 c->input->add_array (&record->secondGlyph, len, record_size);
83 }
84
85 void collect_variation_indices (hb_collect_variation_indices_context_t *c,
86 const ValueFormat *valueFormats) const
87 {
88 unsigned record_size = get_size (valueFormats);
89
90 const PairValueRecord *record = &firstPairValueRecord;
91 unsigned count = len;
92 for (unsigned i = 0; i < count; i++)
93 {
94 if (c->glyph_set->has (record->secondGlyph))
95 { record->collect_variation_indices (c, valueFormats, this); }
96
97 record = &StructAtOffset<const PairValueRecord> (record, record_size);
98 }
99 }
100
101 bool apply (hb_ot_apply_context_t *c,
102 const ValueFormat *valueFormats,
103 unsigned int pos) const
104 {
105 TRACE_APPLY (this);
106 hb_buffer_t *buffer = c->buffer;
107 unsigned int len1 = valueFormats[0].get_len ();
108 unsigned int len2 = valueFormats[1].get_len ();
109 unsigned record_size = get_size (len1, len2);
110
111 const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
112 &firstPairValueRecord,
113 len,
114 record_size);
115 if (record)
116 {
117 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
118 {
119 c->buffer->message (c->font,
120 "try kerning glyphs at %u,%u",
121 c->buffer->idx, pos);
122 }
123
124 bool applied_first = len1 && valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
125 bool applied_second = len2 && valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
126
127 if (applied_first || applied_second)
128 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
129 {
130 c->buffer->message (c->font,
131 "kerned glyphs at %u,%u",
132 c->buffer->idx, pos);
133 }
134
135 if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
136 {
137 c->buffer->message (c->font,
138 "tried kerning glyphs at %u,%u",
139 c->buffer->idx, pos);
140 }
141
142 if (applied_first || applied_second)
143 buffer->unsafe_to_break (buffer->idx, pos + 1);
144
145 if (len2)
146 {
147 pos++;
148 // https://github.com/harfbuzz/harfbuzz/issues/3824
149 // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
150 buffer->unsafe_to_break (buffer->idx, pos + 1);
151 }
152
153 buffer->idx = pos;
154 return_trace (true);
155 }
156 buffer->unsafe_to_concat (buffer->idx, pos + 1);
157 return_trace (false);
158 }
159
160 bool subset (hb_subset_context_t *c,
161 const ValueFormat valueFormats[2],
162 const ValueFormat newFormats[2]) const
163 {
164 TRACE_SUBSET (this);
165 auto snap = c->serializer->snapshot ();
166
167 auto *out = c->serializer->start_embed (*this);
168 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
169 out->len = 0;
170
171 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
172 const hb_map_t &glyph_map = *c->plan->glyph_map;
173
174 unsigned len1 = valueFormats[0].get_len ();
175 unsigned len2 = valueFormats[1].get_len ();
176 unsigned record_size = get_size (len1, len2);
177
178 typename PairValueRecord::context_t context =
179 {
180 this,
181 valueFormats,
182 newFormats,
183 len1,
184 &glyph_map,
185 &c->plan->layout_variation_idx_delta_map
186 };
187
188 const PairValueRecord *record = &firstPairValueRecord;
189 unsigned count = len, num = 0;
190 for (unsigned i = 0; i < count; i++)
191 {
192 if (glyphset.has (record->secondGlyph)
193 && record->subset (c, &context)) num++;
194 record = &StructAtOffset<const PairValueRecord> (record, record_size);
195 }
196
197 out->len = num;
198 if (!num) c->serializer->revert (snap);
199 return_trace (num);
200 }
201};
202
203
204}
205}
206}
207
208#endif // OT_LAYOUT_GPOS_PAIRSET_HH
209