blob: 2214265b5d3160761e5ce252f7096ba2f399c4e3 [file] [log] [blame]
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03301/*
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03302 * Copyright © 2018 Ebrahim Byagowi
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +03303 * Copyright © 2018 Google, Inc.
Ebrahim Byagowi79756c92018-02-19 03:17:44 +03304 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Google Author(s): Behdad Esfahbod
26 */
27
28#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
29#define HB_AAT_LAYOUT_KERX_TABLE_HH
30
Behdad Esfahbodc77ae402018-08-25 22:36:36 -070031#include "hb-open-type.hh"
32#include "hb-aat-layout-common.hh"
Behdad Esfahboded2a4042018-10-07 22:33:41 -040033#include "hb-ot-kern-table.hh"
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033034
Ebrahim Byagowia02c3ee2018-04-12 13:38:19 +043035/*
36 * kerx -- Extended Kerning
37 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
38 */
Ebrahim Byagowi158f2812018-03-26 12:04:30 +043039#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x')
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +033040
41
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033042namespace AAT {
43
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +033044using namespace OT;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033045
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033046
Behdad Esfahbod5d341642018-10-10 18:14:41 -040047struct KerxSubTableHeader
48{
49 inline bool sanitize (hb_sanitize_context_t *c) const
50 {
51 TRACE_SANITIZE (this);
52 return_trace (likely (c->check_struct (this)));
53 }
54
55 public:
56 HBUINT32 length;
57 HBUINT32 coverage;
58 HBUINT32 tupleCount;
59 public:
60 DEFINE_SIZE_STATIC (12);
61};
62
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033063struct KerxSubTableFormat0
64{
Behdad Esfahboded2a4042018-10-07 22:33:41 -040065 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
66 {
67 hb_glyph_pair_t pair = {left, right};
68 int i = pairs.bsearch (pair);
69 if (i == -1)
70 return 0;
71 return pairs[i].get_kerning ();
72 }
73
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -040074 inline bool apply (hb_aat_apply_context_t *c) const
75 {
76 TRACE_APPLY (this);
77
Behdad Esfahbod60f86d32018-10-10 18:10:05 -040078 if (!c->plan->requested_kerning)
79 return false;
80
Behdad Esfahbod53e55942018-10-09 22:35:22 -040081 hb_kern_machine_t<KerxSubTableFormat0> machine (*this);
82
83 machine.kern (c->font, c->buffer, c->plan->kern_mask);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -040084
85 return_trace (true);
86 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033087
88 inline bool sanitize (hb_sanitize_context_t *c) const
89 {
90 TRACE_SANITIZE (this);
Behdad Esfahboded2a4042018-10-07 22:33:41 -040091 return_trace (likely (pairs.sanitize (c)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033092 }
93
94 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -040095 KerxSubTableHeader header;
Behdad Esfahboded2a4042018-10-07 22:33:41 -040096 BinSearchArrayOf<KernPair, HBUINT32>
Behdad Esfahbod5d341642018-10-10 18:14:41 -040097 pairs; /* Sorted kern records. */
Ebrahim Byagowi79756c92018-02-19 03:17:44 +033098 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -040099 DEFINE_SIZE_ARRAY (28, pairs);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330100};
101
102struct KerxSubTableFormat1
103{
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400104 inline bool apply (hb_aat_apply_context_t *c) const
105 {
106 TRACE_APPLY (this);
107
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400108 if (!c->plan->requested_kerning)
109 return false;
110
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400111 /* TODO */
112
113 return_trace (true);
114 }
115
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330116 inline bool sanitize (hb_sanitize_context_t *c) const
117 {
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330118 TRACE_SANITIZE (this);
Ebrahim Byagowi2a4cdfa2018-04-20 21:12:58 +0430119 return_trace (likely (c->check_struct (this) &&
120 stateHeader.sanitize (c)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330121 }
122
123 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400124 KerxSubTableHeader header;
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330125 StateTable<HBUINT16> stateHeader;
126 LOffsetTo<ArrayOf<HBUINT16> > valueTable;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330127 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400128 DEFINE_SIZE_STATIC (32);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330129};
130
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330131struct KerxSubTableFormat2
132{
Behdad Esfahbod1a5a3322018-10-07 23:08:39 -0400133 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
134 const char *end, unsigned int num_glyphs) const
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330135 {
Behdad Esfahbod7727e732018-10-10 13:24:51 -0400136 unsigned int l = (this+leftClassTable).get_value_or_null (left, num_glyphs);
137 unsigned int r = (this+rightClassTable).get_value_or_null (right, num_glyphs);
Behdad Esfahbod8aa83d92018-10-07 22:43:59 -0400138 unsigned int offset = l + r;
Behdad Esfahbod27db8592018-10-09 22:52:41 -0400139 const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
140 if (unlikely ((const char *) v < (const char *) &array ||
141 (const char *) v > (const char *) end - 2))
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330142 return 0;
143 return *v;
144 }
145
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400146 inline bool apply (hb_aat_apply_context_t *c) const
147 {
148 TRACE_APPLY (this);
149
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400150 if (!c->plan->requested_kerning)
151 return false;
152
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400153 accelerator_t accel (*this,
Behdad Esfahbod948f59a2018-10-09 23:07:47 -0400154 c->sanitizer.end,
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400155 c->face->get_num_glyphs ());
156 hb_kern_machine_t<accelerator_t> machine (accel);
157 machine.kern (c->font, c->buffer, c->plan->kern_mask);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400158
159 return_trace (true);
160 }
161
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330162 inline bool sanitize (hb_sanitize_context_t *c) const
163 {
164 TRACE_SANITIZE (this);
Ebrahim Byagowi2a4cdfa2018-04-20 21:12:58 +0430165 return_trace (likely (c->check_struct (this) &&
166 rowWidth.sanitize (c) &&
167 leftClassTable.sanitize (c, this) &&
168 rightClassTable.sanitize (c, this) &&
169 array.sanitize (c, this)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330170 }
171
Behdad Esfahbod53e55942018-10-09 22:35:22 -0400172 struct accelerator_t
173 {
174 const KerxSubTableFormat2 &table;
175 const char *end;
176 unsigned int num_glyphs;
177
178 inline accelerator_t (const KerxSubTableFormat2 &table_,
179 const char *end_, unsigned int num_glyphs_)
180 : table (table_), end (end_), num_glyphs (num_glyphs_) {}
181
182 inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
183 {
184 return table.get_kerning (left, right, end, num_glyphs);
185 }
186 };
187
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330188 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400189 KerxSubTableHeader header;
190 HBUINT32 rowWidth; /* The width, in bytes, of a row in the table. */
Behdad Esfahbodc6bb3a52018-10-07 22:52:53 -0400191 LOffsetTo<Lookup<HBUINT16> >
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400192 leftClassTable; /* Offset from beginning of this subtable to
193 * left-hand class table. */
Behdad Esfahbodc6bb3a52018-10-07 22:52:53 -0400194 LOffsetTo<Lookup<HBUINT16> >
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400195 rightClassTable;/* Offset from beginning of this subtable to
196 * right-hand class table. */
197 LOffsetTo<FWORD> array; /* Offset from beginning of this subtable to
198 * the start of the kerning array. */
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330199 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400200 DEFINE_SIZE_STATIC (28);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330201};
202
203struct KerxSubTableFormat4
204{
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400205 inline bool apply (hb_aat_apply_context_t *c) const
206 {
207 TRACE_APPLY (this);
208
209 /* TODO */
210
211 return_trace (true);
212 }
213
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330214 inline bool sanitize (hb_sanitize_context_t *c) const
215 {
216 TRACE_SANITIZE (this);
Behdad Esfahbodc6bb3a52018-10-07 22:52:53 -0400217
218 /* TODO */
219 return_trace (likely (c->check_struct (this)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330220 }
221
222 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400223 KerxSubTableHeader header;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330224 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400225 DEFINE_SIZE_STATIC (12);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330226};
227
228struct KerxSubTableFormat6
229{
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400230 inline bool apply (hb_aat_apply_context_t *c) const
231 {
232 TRACE_APPLY (this);
233
Behdad Esfahbod60f86d32018-10-10 18:10:05 -0400234 if (!c->plan->requested_kerning)
235 return false;
236
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400237 /* TODO */
238
239 return_trace (true);
240 }
241
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330242 inline bool sanitize (hb_sanitize_context_t *c) const
243 {
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330244 TRACE_SANITIZE (this);
Ebrahim Byagowi2a4cdfa2018-04-20 21:12:58 +0430245 return_trace (likely (c->check_struct (this) &&
246 rowIndexTable.sanitize (c, this) &&
247 columnIndexTable.sanitize (c, this) &&
248 kerningArray.sanitize (c, this) &&
249 kerningVector.sanitize (c, this)));
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330250 }
251
252 protected:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400253 KerxSubTableHeader header;
254 HBUINT32 flags;
255 HBUINT16 rowCount;
256 HBUINT16 columnCount;
Ebrahim Byagowi71b45982018-03-02 11:04:09 +0330257 LOffsetTo<Lookup<HBUINT16> > rowIndexTable;
258 LOffsetTo<Lookup<HBUINT16> > columnIndexTable;
259 LOffsetTo<Lookup<HBUINT16> > kerningArray;
260 LOffsetTo<Lookup<HBUINT16> > kerningVector;
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330261 public:
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400262 DEFINE_SIZE_STATIC (36);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330263};
264
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330265struct KerxTable
266{
Behdad Esfahbode640f3a2018-10-09 08:28:07 -0400267 friend struct kerx;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400268
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400269 inline unsigned int get_size (void) const { return u.header.length; }
270 inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400271
272 enum Coverage
273 {
274 Vertical = 0x80000000, /* Set if table has vertical kerning values. */
275 CrossStream = 0x40000000, /* Set if table has cross-stream kerning values. */
276 Variation = 0x20000000, /* Set if table has variation kerning values. */
Behdad Esfahbod362d3242018-10-09 23:27:00 -0400277 Backwards = 0x10000000, /* If clear, process the glyphs forwards, that
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400278 * is, from first to last in the glyph stream.
279 * If we, process them from last to first.
280 * This flag only applies to state-table based
281 * 'kerx' subtables (types 1 and 4). */
282 Reserved = 0x0FFFFF00, /* Reserved, set to zero. */
283 SubtableType = 0x000000FF, /* Subtable type. */
284 };
285
286 template <typename context_t>
287 inline typename context_t::return_t dispatch (context_t *c) const
288 {
289 unsigned int subtable_type = get_type ();
290 TRACE_DISPATCH (this, subtable_type);
291 switch (subtable_type) {
292 case 0 : return_trace (c->dispatch (u.format0));
293 case 1 : return_trace (c->dispatch (u.format1));
294 case 2 : return_trace (c->dispatch (u.format2));
295 case 4 : return_trace (c->dispatch (u.format4));
296 case 6 : return_trace (c->dispatch (u.format6));
297 default: return_trace (c->default_return_value ());
298 }
299 }
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330300
301 inline bool sanitize (hb_sanitize_context_t *c) const
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330302 {
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330303 TRACE_SANITIZE (this);
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400304 if (!u.header.sanitize (c) ||
305 !c->check_range (this, u.header.length))
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330306 return_trace (false);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330307
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400308 return_trace (dispatch (c));
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330309 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330310
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330311protected:
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330312 union {
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400313 KerxSubTableHeader header;
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330314 KerxSubTableFormat0 format0;
315 KerxSubTableFormat1 format1;
316 KerxSubTableFormat2 format2;
317 KerxSubTableFormat4 format4;
318 KerxSubTableFormat6 format6;
319 } u;
320public:
321 DEFINE_SIZE_MIN (12);
322};
323
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400324
325/*
326 * The 'kerx' Table
327 */
328
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330329struct kerx
330{
Ebrahim Byagowi158f2812018-03-26 12:04:30 +0430331 static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330332
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400333 inline bool has_data (void) const { return version != 0; }
334
335 inline void apply (hb_aat_apply_context_t *c) const
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330336 {
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400337 c->set_lookup_index (0);
338 const KerxTable *table = &firstTable;
339 unsigned int count = tableCount;
340 for (unsigned int i = 0; i < count; i++)
341 {
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400342 bool reverse;
343
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400344 if (table->u.header.coverage & (KerxTable::CrossStream | KerxTable::Variation))
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400345 goto skip; /* We do NOT handle cross-stream or variation kerning. */
346
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400347 if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400348 bool (table->u.header.coverage & KerxTable::Vertical))
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400349 goto skip;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400350
Behdad Esfahbod5d341642018-10-10 18:14:41 -0400351 reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400352 HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
353
354 if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400355 goto skip;
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400356
357 if (reverse)
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400358 c->buffer->reverse ();
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400359
Behdad Esfahbod948f59a2018-10-09 23:07:47 -0400360 c->sanitizer.set_object (*table);
361
Behdad Esfahbodad763072018-10-09 22:57:00 -0400362 /* XXX Reverse-kern is not working yet...
363 * hb_kern_machine_t would need to know that it's reverse-kerning.
364 * Or better yet, make it work in reverse as well, so we don't have
365 * to reverse and reverse back? */
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400366 table->dispatch (c);
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400367
368 if (reverse)
Behdad Esfahbod44f09af2018-10-10 17:32:32 -0400369 c->buffer->reverse ();
Behdad Esfahbod80e31022018-10-08 22:41:08 -0400370
371 (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
372
373 skip:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400374 table = &StructAfter<KerxTable> (*table);
375 }
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330376 }
377
378 inline bool sanitize (hb_sanitize_context_t *c) const
379 {
380 TRACE_SANITIZE (this);
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400381 if (!version.sanitize (c) || version < 2 ||
382 !tableCount.sanitize (c))
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330383 return_trace (false);
384
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400385 const KerxTable *table = &firstTable;
386 unsigned int count = tableCount;
387 for (unsigned int i = 0; i < count; i++)
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330388 {
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400389 if (!table->sanitize (c))
390 return_trace (false);
Ebrahim Byagowib73a5a12018-03-02 00:07:26 +0330391 table = &StructAfter<KerxTable> (*table);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330392 }
393
394 return_trace (true);
395 }
396
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330397 protected:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400398 HBUINT16 version; /* The version number of the extended kerning table
399 * (currently 2, 3, or 4). */
400 HBUINT16 unused; /* Set to 0. */
401 HBUINT32 tableCount; /* The number of subtables included in the extended kerning
402 * table. */
403 KerxTable firstTable; /* Subtables. */
404/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */
405
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330406 public:
Behdad Esfahbodfdce1e12018-10-07 14:01:33 -0400407 DEFINE_SIZE_MIN (8);
Ebrahim Byagowi79756c92018-02-19 03:17:44 +0330408};
409
410} /* namespace AAT */
411
412
413#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */