hb-ot-layout-gsub-table.hh 41.8 KB
Newer Older
B
Behdad Esfahbod 已提交
1
/*
B
Behdad Esfahbod 已提交
2
 * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
3
 * Copyright © 2010,2012,2013  Google, Inc.
B
Behdad Esfahbod 已提交
4
 *
B
Behdad Esfahbod 已提交
5
 *  This is part of HarfBuzz, a text shaping library.
B
Behdad Esfahbod 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Red Hat Author(s): Behdad Esfahbod
26
 * Google Author(s): Behdad Esfahbod
B
Behdad Esfahbod 已提交
27 28
 */

29 30
#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
#define HB_OT_LAYOUT_GSUB_TABLE_HH
B
Behdad Esfahbod 已提交
31

32
#include "hb-ot-layout-gsubgpos.hh"
B
Behdad Esfahbod 已提交
33

B
Behdad Esfahbod 已提交
34

35 36
namespace OT {

37

38
static inline void SingleSubst_serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
39
					  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
40
					  hb_array_t<const GlyphID> substitutes);
41

B
Behdad Esfahbod 已提交
42 43
struct SingleSubstFormat1
{
44
  bool intersects (const hb_set_t *glyphs) const
45 46
  { return (this+coverage).intersects (glyphs); }

47
  void closure (hb_closure_context_t *c) const
48
  {
49 50 51 52 53
    + hb_iter (this+coverage)
    | hb_filter (*c->glyphs)
    | hb_map ([&] (hb_codepoint_t g) -> hb_codepoint_t { return (g + deltaGlyphID) & 0xFFFFu; })
    | hb_sink (c->output)
    ;
54 55
  }

56
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
57
  {
58
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
B
Behdad Esfahbod 已提交
59

60 61 62 63
    + hb_iter (this+coverage)
    | hb_map ([&] (hb_codepoint_t g) -> hb_codepoint_t { return (g + deltaGlyphID) & 0xFFFFu; })
    | hb_sink (c->output)
    ;
64 65
  }

66
  const Coverage &get_coverage () const { return this+coverage; }
67

68
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
69
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
70

71
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
72
  {
B
Behdad Esfahbod 已提交
73
    TRACE_APPLY (this);
74
    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
75
    unsigned int index = (this+coverage).get_coverage (glyph_id);
B
Behdad Esfahbod 已提交
76
    if (likely (index == NOT_COVERED)) return_trace (false);
B
Behdad Esfahbod 已提交
77

78 79
    /* According to the Adobe Annotated OpenType Suite, result is always
     * limited to 16bit. */
80
    glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
81
    c->replace_glyph (glyph_id);
B
Behdad Esfahbod 已提交
82

B
Behdad Esfahbod 已提交
83
    return_trace (true);
84
  }
B
Behdad Esfahbod 已提交
85

86
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
87
		  hb_sorted_array_t<const GlyphID> glyphs,
88
		  unsigned delta)
89
  {
B
Behdad Esfahbod 已提交
90
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
91
    if (unlikely (!c->extend_min (*this))) return_trace (false);
B
Behdad Esfahbod 已提交
92
    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
93
    c->check_assign (deltaGlyphID, delta);
B
Behdad Esfahbod 已提交
94
    return_trace (true);
95 96
  }

97
  bool subset (hb_subset_context_t *c) const
98 99
  {
    TRACE_SUBSET (this);
100
    const hb_set_t &glyphset = *c->plan->glyphset ();
101
    const hb_map_t &glyph_map = *c->plan->glyph_map;
102

B
Behdad Esfahbod 已提交
103
    hb_sorted_vector_t<GlyphID> from;
104
    hb_vector_t<GlyphID> to;
105
    hb_codepoint_t delta = deltaGlyphID;
106 107 108 109 110 111 112 113

    + hb_iter (this+coverage)
    | hb_filter (glyphset)
    | hb_map ([&] (hb_codepoint_t g) -> hb_pair_t<hb_codepoint_t, hb_codepoint_t>
	      { return hb_pair<hb_codepoint_t, hb_codepoint_t> (glyph_map[g],
								glyph_map[(g + delta) & 0xFFFF]); })
    | hb_unzip (from, to);

114
    c->serializer->propagate_error (from, to);
B
Behdad Esfahbod 已提交
115
    SingleSubst_serialize (c->serializer, from, to);
116
    return_trace (from.length);
117 118
  }

119
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
120
  {
B
Behdad Esfahbod 已提交
121
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
122
    return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
B
Behdad Esfahbod 已提交
123 124
  }

125
  protected:
B
Behdad Esfahbod 已提交
126
  HBUINT16	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
127 128
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
129
					 * beginning of Substitution table */
130 131
  HBUINT16	deltaGlyphID;		/* Add to original GlyphID to get
					 * substitute GlyphID, modulo 0x10000 */
132 133
  public:
  DEFINE_SIZE_STATIC (6);
B
Behdad Esfahbod 已提交
134 135
};

B
Behdad Esfahbod 已提交
136 137
struct SingleSubstFormat2
{
138
  bool intersects (const hb_set_t *glyphs) const
139 140
  { return (this+coverage).intersects (glyphs); }

141
  void closure (hb_closure_context_t *c) const
142
  {
B
Behdad Esfahbod 已提交
143 144 145
    + hb_zip (this+coverage, substitute)
    | hb_filter (*c->glyphs, hb_first)
    | hb_map (hb_second)
146
    | hb_sink (c->output)
147
    ;
148 149
  }

150
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
151
  {
152
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
B
Behdad Esfahbod 已提交
153 154
    + hb_zip (this+coverage, substitute)
    | hb_map (hb_second)
155
    | hb_sink (c->output)
156
    ;
157 158
  }

159
  const Coverage &get_coverage () const { return this+coverage; }
160

161
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
162
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
163

164
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
165
  {
B
Behdad Esfahbod 已提交
166
    TRACE_APPLY (this);
167
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
B
Behdad Esfahbod 已提交
168
    if (likely (index == NOT_COVERED)) return_trace (false);
169

B
Behdad Esfahbod 已提交
170
    if (unlikely (index >= substitute.len)) return_trace (false);
171

172
    c->replace_glyph (substitute[index]);
B
Behdad Esfahbod 已提交
173

B
Behdad Esfahbod 已提交
174
    return_trace (true);
175
  }
B
Behdad Esfahbod 已提交
176

177
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
178
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
179
		  hb_array_t<const GlyphID> substitutes)
B
Behdad Esfahbod 已提交
180
  {
B
Behdad Esfahbod 已提交
181
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
182
    if (unlikely (!c->extend_min (*this))) return_trace (false);
B
Behdad Esfahbod 已提交
183 184
    if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
B
Behdad Esfahbod 已提交
185
    return_trace (true);
B
Behdad Esfahbod 已提交
186 187
  }

188
  bool subset (hb_subset_context_t *c) const
189 190
  {
    TRACE_SUBSET (this);
191
    const hb_set_t &glyphset = *c->plan->glyphset ();
192
    const hb_map_t &glyph_map = *c->plan->glyph_map;
193

B
Behdad Esfahbod 已提交
194
    hb_sorted_vector_t<GlyphID> from;
195
    hb_vector_t<GlyphID> to;
196 197 198 199 200 201 202

    + hb_zip (this+coverage, substitute)
    | hb_filter (glyphset, hb_first)
    | hb_map ([&] (hb_pair_t<hb_codepoint_t, const GlyphID &> p) -> hb_pair_t<hb_codepoint_t, hb_codepoint_t>
	      { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
    | hb_unzip (from, to);

203
    c->serializer->propagate_error (from, to);
B
Behdad Esfahbod 已提交
204
    SingleSubst_serialize (c->serializer, from, to);
205
    return_trace (from.length);
206 207
  }

208
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
209
  {
B
Behdad Esfahbod 已提交
210
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
211
    return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
B
Behdad Esfahbod 已提交
212 213
  }

214
  protected:
B
Behdad Esfahbod 已提交
215
  HBUINT16	format;			/* Format identifier--format = 2 */
B
Behdad Esfahbod 已提交
216 217
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
218
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
219 220 221
  ArrayOf<GlyphID>
		substitute;		/* Array of substitute
					 * GlyphIDs--ordered by Coverage Index */
222
  public:
223
  DEFINE_SIZE_ARRAY (6, substitute);
B
Behdad Esfahbod 已提交
224 225
};

B
Behdad Esfahbod 已提交
226 227
struct SingleSubst
{
228
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
229
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
230
		  hb_array_t<const GlyphID> substitutes)
B
Behdad Esfahbod 已提交
231
  {
B
Behdad Esfahbod 已提交
232
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
233
    if (unlikely (!c->extend_min (u.format))) return_trace (false);
B
Behdad Esfahbod 已提交
234 235
    unsigned format = 2;
    unsigned delta = 0;
236
    if (glyphs.length)
B
Behdad Esfahbod 已提交
237
    {
B
Behdad Esfahbod 已提交
238
      format = 1;
B
Behdad Esfahbod 已提交
239
      delta = (unsigned) (substitutes[0] - glyphs[0]) & 0xFFFF;
240
      for (unsigned int i = 1; i < glyphs.length; i++)
B
Behdad Esfahbod 已提交
241
	if (delta != ((unsigned) (substitutes[i] - glyphs[i]) & 0xFFFF)) {
B
Behdad Esfahbod 已提交
242 243 244 245
	  format = 2;
	  break;
	}
    }
246
    u.format = format;
B
Behdad Esfahbod 已提交
247
    switch (u.format) {
B
Behdad Esfahbod 已提交
248 249
    case 1: return_trace (u.format1.serialize (c, glyphs, delta));
    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes));
B
Behdad Esfahbod 已提交
250
    default:return_trace (false);
B
Behdad Esfahbod 已提交
251 252 253
    }
  }

254
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
255
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
B
Behdad Esfahbod 已提交
256
  {
257
    TRACE_DISPATCH (this, u.format);
258
    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
B
Behdad Esfahbod 已提交
259
    switch (u.format) {
260 261
    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
    case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
262
    default:return_trace (c->default_return_value ());
B
Behdad Esfahbod 已提交
263 264 265
    }
  }

266
  protected:
267
  union {
B
Behdad Esfahbod 已提交
268
  HBUINT16		format;		/* Format identifier */
B
Behdad Esfahbod 已提交
269 270
  SingleSubstFormat1	format1;
  SingleSubstFormat2	format2;
271
  } u;
B
Behdad Esfahbod 已提交
272
};
273

274 275
static inline void
SingleSubst_serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
276
		       hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
277 278
		       hb_array_t<const GlyphID> substitutes)
{ c->start_embed<SingleSubst> ()->serialize (c, glyphs, substitutes); }
B
Behdad Esfahbod 已提交
279

B
Behdad Esfahbod 已提交
280 281
struct Sequence
{
282
  void closure (hb_closure_context_t *c) const
283
  {
B
Behdad Esfahbod 已提交
284 285
    unsigned int count = substitute.len;
    for (unsigned int i = 0; i < count; i++)
B
Behdad Esfahbod 已提交
286
      c->output->add (substitute[i]);
287 288
  }

289
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
B
Behdad Esfahbod 已提交
290
  { c->output->add_array (substitute.arrayZ, substitute.len); }
291

292
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
293
  {
B
Behdad Esfahbod 已提交
294
    TRACE_APPLY (this);
B
Behdad Esfahbod 已提交
295
    unsigned int count = substitute.len;
296

B
Behdad Esfahbod 已提交
297 298 299
    /* Special-case to make it in-place and not consider this
     * as a "multiplied" substitution. */
    if (unlikely (count == 1))
300
    {
301
      c->replace_glyph (substitute.arrayZ[0]);
B
Behdad Esfahbod 已提交
302
      return_trace (true);
303
    }
304
    /* Spec disallows this, but Uniscribe allows it.
305
     * https://github.com/harfbuzz/harfbuzz/issues/253 */
306 307 308 309 310
    else if (unlikely (count == 0))
    {
      c->buffer->delete_glyph ();
      return_trace (true);
    }
B
Behdad Esfahbod 已提交
311 312 313 314 315 316

    unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
			 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;

    for (unsigned int i = 0; i < count; i++) {
      _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
317
      c->output_glyph_for_component (substitute.arrayZ[i], klass);
318
    }
B
Behdad Esfahbod 已提交
319
    c->buffer->skip_glyph ();
B
Behdad Esfahbod 已提交
320

B
Behdad Esfahbod 已提交
321
    return_trace (true);
B
Behdad Esfahbod 已提交
322 323
  }

324
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
325
		  hb_array_t<const GlyphID> subst)
326
  {
B
Behdad Esfahbod 已提交
327
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
328
    return_trace (substitute.serialize (c, subst));
329 330
  }

331
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
332
  {
B
Behdad Esfahbod 已提交
333
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
334
    return_trace (substitute.sanitize (c));
B
Behdad Esfahbod 已提交
335 336
  }

337
  protected:
B
Behdad Esfahbod 已提交
338 339
  ArrayOf<GlyphID>
		substitute;		/* String of GlyphIDs to substitute */
340
  public:
341
  DEFINE_SIZE_ARRAY (2, substitute);
B
Behdad Esfahbod 已提交
342 343
};

B
Behdad Esfahbod 已提交
344 345
struct MultipleSubstFormat1
{
346
  bool intersects (const hb_set_t *glyphs) const
347 348
  { return (this+coverage).intersects (glyphs); }

349
  void closure (hb_closure_context_t *c) const
350
  {
351 352 353
    + hb_zip (this+coverage, sequence)
    | hb_filter (*c->glyphs, hb_first)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
354
    | hb_apply ([&] (const OffsetTo<Sequence> &_) { (this+_).closure (c); })
355
    ;
356 357
  }

358
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
359
  {
360
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
361 362
    + hb_zip (this+coverage, sequence)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
363
    | hb_apply ([&] (const OffsetTo<Sequence> &_) { (this+_).collect_glyphs (c); })
364
    ;
365 366
  }

367
  const Coverage &get_coverage () const { return this+coverage; }
368

369
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
370
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
371

372
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
373
  {
B
Behdad Esfahbod 已提交
374
    TRACE_APPLY (this);
375

376
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
B
Behdad Esfahbod 已提交
377
    if (likely (index == NOT_COVERED)) return_trace (false);
378

B
Behdad Esfahbod 已提交
379
    return_trace ((this+sequence[index]).apply (c));
380
  }
B
Behdad Esfahbod 已提交
381

382
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
383
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
384 385
		  hb_array_t<const unsigned int> substitute_len_list,
		  hb_array_t<const GlyphID> substitute_glyphs_list)
386
  {
B
Behdad Esfahbod 已提交
387
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
388
    if (unlikely (!c->extend_min (*this))) return_trace (false);
389 390
    if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
    for (unsigned int i = 0; i < glyphs.length; i++)
B
Behdad Esfahbod 已提交
391 392 393 394 395 396 397 398
    {
      unsigned int substitute_len = substitute_len_list[i];
      if (unlikely (!sequence[i].serialize (c, this)
				.serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
	return_trace (false);
      substitute_glyphs_list += substitute_len;
    }
    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
399 400
  }

401
  bool subset (hb_subset_context_t *c) const
402 403 404 405 406 407
  {
    TRACE_SUBSET (this);
    // TODO(subset)
    return_trace (false);
  }

408
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
409
  {
B
Behdad Esfahbod 已提交
410
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
411
    return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
B
Behdad Esfahbod 已提交
412 413
  }

414
  protected:
B
Behdad Esfahbod 已提交
415
  HBUINT16	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
416 417
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
418
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
419 420 421
  OffsetArrayOf<Sequence>
		sequence;		/* Array of Sequence tables
					 * ordered by Coverage Index */
422
  public:
423
  DEFINE_SIZE_ARRAY (6, sequence);
B
Behdad Esfahbod 已提交
424
};
425

B
Behdad Esfahbod 已提交
426 427
struct MultipleSubst
{
428
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
429
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
430 431
		  hb_array_t<const unsigned int> substitute_len_list,
		  hb_array_t<const GlyphID> substitute_glyphs_list)
432
  {
B
Behdad Esfahbod 已提交
433
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
434
    if (unlikely (!c->extend_min (u.format))) return_trace (false);
435
    unsigned int format = 1;
436
    u.format = format;
437
    switch (u.format) {
B
Behdad Esfahbod 已提交
438
    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
B
Behdad Esfahbod 已提交
439
    default:return_trace (false);
440 441 442
    }
  }

443
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
444
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
B
Behdad Esfahbod 已提交
445
  {
446
    TRACE_DISPATCH (this, u.format);
447
    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
B
Behdad Esfahbod 已提交
448
    switch (u.format) {
449
    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
450
    default:return_trace (c->default_return_value ());
B
Behdad Esfahbod 已提交
451 452 453
    }
  }

454
  protected:
455
  union {
B
Behdad Esfahbod 已提交
456
  HBUINT16		format;		/* Format identifier */
B
Behdad Esfahbod 已提交
457
  MultipleSubstFormat1	format1;
458 459 460
  } u;
};

461 462
struct AlternateSet
{
463
  void closure (hb_closure_context_t *c) const
464 465 466
  {
    unsigned int count = alternates.len;
    for (unsigned int i = 0; i < count; i++)
B
Behdad Esfahbod 已提交
467
      c->output->add (alternates[i]);
468 469
  }

470
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
B
Behdad Esfahbod 已提交
471
  { c->output->add_array (alternates.arrayZ, alternates.len); }
472

473
  bool apply (hb_ot_apply_context_t *c) const
474 475 476 477 478 479
  {
    TRACE_APPLY (this);
    unsigned int count = alternates.len;

    if (unlikely (!count)) return_trace (false);

480 481
    hb_mask_t glyph_mask = c->buffer->cur().mask;
    hb_mask_t lookup_mask = c->lookup_mask;
482

483 484 485
    /* Note: This breaks badly if two features enabled this lookup together. */
    unsigned int shift = hb_ctz (lookup_mask);
    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
486

487
    /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
488
    if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
B
Behdad Esfahbod 已提交
489
      alt_index = c->random_number () % count + 1;
B
Behdad Esfahbod 已提交
490

491 492
    if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);

493 494
    c->replace_glyph (alternates[alt_index - 1]);

495 496 497
    return_trace (true);
  }

498
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
499
		  hb_array_t<const GlyphID> alts)
500 501
  {
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
502
    return_trace (alternates.serialize (c, alts));
503 504
  }

505
  bool sanitize (hb_sanitize_context_t *c) const
506 507 508 509 510 511 512 513
  {
    TRACE_SANITIZE (this);
    return_trace (alternates.sanitize (c));
  }

  protected:
  ArrayOf<GlyphID>
		alternates;		/* Array of alternate GlyphIDs--in
B
Behdad Esfahbod 已提交
514
					 * arbitrary order */
515 516 517
  public:
  DEFINE_SIZE_ARRAY (2, alternates);
};
B
Behdad Esfahbod 已提交
518

B
Behdad Esfahbod 已提交
519 520
struct AlternateSubstFormat1
{
521
  bool intersects (const hb_set_t *glyphs) const
522 523
  { return (this+coverage).intersects (glyphs); }

524
  void closure (hb_closure_context_t *c) const
525
  {
526 527
    + hb_zip (this+coverage, alternateSet)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
528
    | hb_apply ([&] (const OffsetTo<AlternateSet> &_) { (this+_).closure (c); })
529
    ;
530 531
  }

532
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
533
  {
534
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
535 536
    + hb_zip (this+coverage, alternateSet)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
537
    | hb_apply ([&] (const OffsetTo<AlternateSet> &_) { (this+_).collect_glyphs (c); })
538
    ;
539 540
  }

541
  const Coverage &get_coverage () const { return this+coverage; }
542

543
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
544
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
545

546
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
547
  {
B
Behdad Esfahbod 已提交
548
    TRACE_APPLY (this);
549

550
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
B
Behdad Esfahbod 已提交
551
    if (likely (index == NOT_COVERED)) return_trace (false);
552

553
    return_trace ((this+alternateSet[index]).apply (c));
554
  }
B
Behdad Esfahbod 已提交
555

556
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
557
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
558 559
		  hb_array_t<const unsigned int> alternate_len_list,
		  hb_array_t<const GlyphID> alternate_glyphs_list)
560
  {
B
Behdad Esfahbod 已提交
561
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
562
    if (unlikely (!c->extend_min (*this))) return_trace (false);
563 564
    if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
    for (unsigned int i = 0; i < glyphs.length; i++)
B
Behdad Esfahbod 已提交
565 566 567 568 569 570 571 572
    {
      unsigned int alternate_len = alternate_len_list[i];
      if (unlikely (!alternateSet[i].serialize (c, this)
				    .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
	return_trace (false);
      alternate_glyphs_list += alternate_len;
    }
    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
573 574
  }

575
  bool subset (hb_subset_context_t *c) const
576 577 578 579 580 581
  {
    TRACE_SUBSET (this);
    // TODO(subset)
    return_trace (false);
  }

582
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
583
  {
B
Behdad Esfahbod 已提交
584
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
585
    return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
B
Behdad Esfahbod 已提交
586 587
  }

588
  protected:
B
Behdad Esfahbod 已提交
589
  HBUINT16	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
590 591
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
B
Behdad Esfahbod 已提交
592
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
593 594 595
  OffsetArrayOf<AlternateSet>
		alternateSet;		/* Array of AlternateSet tables
					 * ordered by Coverage Index */
596
  public:
597
  DEFINE_SIZE_ARRAY (6, alternateSet);
B
Behdad Esfahbod 已提交
598
};
599

B
Behdad Esfahbod 已提交
600 601
struct AlternateSubst
{
602
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
603
		  hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
604 605
		  hb_array_t<const unsigned int> alternate_len_list,
		  hb_array_t<const GlyphID> alternate_glyphs_list)
606
  {
B
Behdad Esfahbod 已提交
607
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
608
    if (unlikely (!c->extend_min (u.format))) return_trace (false);
609
    unsigned int format = 1;
610
    u.format = format;
611
    switch (u.format) {
B
Behdad Esfahbod 已提交
612
    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
B
Behdad Esfahbod 已提交
613
    default:return_trace (false);
614 615 616
    }
  }

617
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
618
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
B
Behdad Esfahbod 已提交
619
  {
620
    TRACE_DISPATCH (this, u.format);
621
    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
B
Behdad Esfahbod 已提交
622
    switch (u.format) {
623
    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
624
    default:return_trace (c->default_return_value ());
B
Behdad Esfahbod 已提交
625 626 627
    }
  }

628
  protected:
629
  union {
B
Behdad Esfahbod 已提交
630
  HBUINT16		format;		/* Format identifier */
B
Behdad Esfahbod 已提交
631
  AlternateSubstFormat1	format1;
632 633 634
  } u;
};

635

B
Behdad Esfahbod 已提交
636 637
struct Ligature
{
638
  bool intersects (const hb_set_t *glyphs) const
639
  {
640
    unsigned int count = component.lenP1;
641 642 643 644 645 646
    for (unsigned int i = 1; i < count; i++)
      if (!glyphs->has (component[i]))
        return false;
    return true;
  }

647
  void closure (hb_closure_context_t *c) const
648
  {
B
Behdad Esfahbod 已提交
649
    if (!intersects (c->glyphs)) return;
B
Behdad Esfahbod 已提交
650
    c->output->add (ligGlyph);
651 652
  }

653
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
654
  {
655
    c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
B
Minor  
Behdad Esfahbod 已提交
656
    c->output->add (ligGlyph);
657 658
  }

659
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
660
  {
661
    if (c->len != component.lenP1)
B
Behdad Esfahbod 已提交
662
      return false;
663 664 665

    for (unsigned int i = 1; i < c->len; i++)
      if (likely (c->glyphs[i] != component[i]))
B
Behdad Esfahbod 已提交
666
	return false;
667

B
Behdad Esfahbod 已提交
668
    return true;
B
Behdad Esfahbod 已提交
669 670
  }

671
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
672
  {
B
Behdad Esfahbod 已提交
673
    TRACE_APPLY (this);
674
    unsigned int count = component.lenP1;
B
Behdad Esfahbod 已提交
675

B
Behdad Esfahbod 已提交
676
    if (unlikely (!count)) return_trace (false);
B
Behdad Esfahbod 已提交
677

678 679 680 681 682
    /* Special-case to make it in-place and not consider this
     * as a "ligated" substitution. */
    if (unlikely (count == 1))
    {
      c->replace_glyph (ligGlyph);
B
Behdad Esfahbod 已提交
683
      return_trace (true);
684 685
    }

B
Behdad Esfahbod 已提交
686
    unsigned int total_component_count = 0;
687

688
    unsigned int match_length = 0;
689
    unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
690

691 692 693
    if (likely (!match_input (c, count,
			      &component[1],
			      match_glyph,
B
Behdad Esfahbod 已提交
694
			      nullptr,
695 696
			      &match_length,
			      match_positions,
697
			      &total_component_count)))
B
Behdad Esfahbod 已提交
698
      return_trace (false);
699

700 701
    ligate_input (c,
		  count,
702 703
		  match_positions,
		  match_length,
704
		  ligGlyph,
705
		  total_component_count);
706

B
Behdad Esfahbod 已提交
707
    return_trace (true);
708
  }
709

710 711
  bool serialize (hb_serialize_context_t *c,
		  GlyphID ligature,
B
Behdad Esfahbod 已提交
712
		  hb_array_t<const GlyphID> components /* Starting from second */)
713
  {
B
Behdad Esfahbod 已提交
714
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
715
    if (unlikely (!c->extend_min (*this))) return_trace (false);
716
    ligGlyph = ligature;
B
Behdad Esfahbod 已提交
717
    if (unlikely (!component.serialize (c, components))) return_trace (false);
B
Behdad Esfahbod 已提交
718
    return_trace (true);
719 720
  }

B
Behdad Esfahbod 已提交
721
  public:
722
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
723
  {
B
Behdad Esfahbod 已提交
724
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
725
    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
B
Behdad Esfahbod 已提交
726 727
  }

728
  protected:
729
  GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
B
Behdad Esfahbod 已提交
730 731
  HeadlessArrayOf<GlyphID>
		component;		/* Array of component GlyphIDs--start
732 733
					 * with the second  component--ordered
					 * in writing direction */
734
  public:
735
  DEFINE_SIZE_ARRAY (4, component);
736
};
B
Behdad Esfahbod 已提交
737

B
Behdad Esfahbod 已提交
738 739
struct LigatureSet
{
740
  bool intersects (const hb_set_t *glyphs) const
741
  {
B
Behdad Esfahbod 已提交
742 743 744 745 746
    return
    + hb_iter (ligature)
    | hb_map ([&] (const OffsetTo<Ligature> &_) -> bool { return (this+_).intersects (glyphs); })
    | hb_any
    ;
747 748
  }

749
  void closure (hb_closure_context_t *c) const
750
  {
B
Behdad Esfahbod 已提交
751 752 753
    + hb_iter (ligature)
    | hb_apply ([&] (const OffsetTo<Ligature> &_) { (this+_).closure (c); })
    ;
754 755
  }

756
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
757
  {
B
Behdad Esfahbod 已提交
758 759 760
    + hb_iter (ligature)
    | hb_apply ([&] (const OffsetTo<Ligature> &_) { (this+_).collect_glyphs (c); })
    ;
761 762
  }

763
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
764
  {
B
Behdad Esfahbod 已提交
765 766 767 768 769
    return
    + hb_iter (ligature)
    | hb_map ([&] (const OffsetTo<Ligature> &_) -> bool { return (this+_).would_apply (c); })
    | hb_any
    ;
B
Behdad Esfahbod 已提交
770 771
  }

772
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
773
  {
B
Behdad Esfahbod 已提交
774
    TRACE_APPLY (this);
B
Behdad Esfahbod 已提交
775
    unsigned int num_ligs = ligature.len;
B
Behdad Esfahbod 已提交
776 777
    for (unsigned int i = 0; i < num_ligs; i++)
    {
B
Behdad Esfahbod 已提交
778
      const Ligature &lig = this+ligature[i];
B
Behdad Esfahbod 已提交
779
      if (lig.apply (c)) return_trace (true);
780 781
    }

B
Behdad Esfahbod 已提交
782
    return_trace (false);
783
  }
B
Behdad Esfahbod 已提交
784

785
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
786 787
		  hb_array_t<const GlyphID> ligatures,
		  hb_array_t<const unsigned int> component_count_list,
788
		  hb_array_t<const GlyphID> &component_list /* Starting from second for each ligature */)
789
  {
B
Behdad Esfahbod 已提交
790
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
791
    if (unlikely (!c->extend_min (*this))) return_trace (false);
792 793
    if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
    for (unsigned int i = 0; i < ligatures.length; i++)
B
Behdad Esfahbod 已提交
794
    {
795
      unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
B
Behdad Esfahbod 已提交
796 797 798 799 800 801 802
      if (unlikely (!ligature[i].serialize (c, this)
				.serialize (c,
					    ligatures[i],
					    component_list.sub_array (0, component_count))))
	return_trace (false);
      component_list += component_count;
    }
B
Behdad Esfahbod 已提交
803
    return_trace (true);
804 805
  }

806
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
807
  {
B
Behdad Esfahbod 已提交
808
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
809
    return_trace (ligature.sanitize (c, this));
B
Behdad Esfahbod 已提交
810 811
  }

812
  protected:
B
Behdad Esfahbod 已提交
813 814 815
  OffsetArrayOf<Ligature>
		ligature;		/* Array LigatureSet tables
					 * ordered by preference */
816
  public:
817
  DEFINE_SIZE_ARRAY (2, ligature);
B
Behdad Esfahbod 已提交
818 819
};

B
Behdad Esfahbod 已提交
820 821
struct LigatureSubstFormat1
{
822
  bool intersects (const hb_set_t *glyphs) const
823
  {
824 825 826 827 828 829 830 831
    return
    + hb_zip (this+coverage, ligatureSet)
    | hb_filter (*glyphs, hb_first)
    | hb_map (hb_second)
    | hb_map ([&] (const OffsetTo<LigatureSet> &_) -> bool
	      { return (this+_).intersects (glyphs); })
    | hb_any
    ;
832 833
  }

834
  void closure (hb_closure_context_t *c) const
835
  {
836 837 838
    + hb_zip (this+coverage, ligatureSet)
    | hb_filter (*c->glyphs, hb_first)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
839
    | hb_apply ([&] (const OffsetTo<LigatureSet> &_) { (this+_).closure (c); })
840
    ;
841 842
  }

843
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
844
  {
845
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
B
Behdad Esfahbod 已提交
846

847 848
    + hb_zip (this+coverage, ligatureSet)
    | hb_map (hb_second)
B
Behdad Esfahbod 已提交
849
    | hb_apply ([&] (const OffsetTo<LigatureSet> &_) { (this+_).collect_glyphs (c); })
850
    ;
851 852
  }

853
  const Coverage &get_coverage () const { return this+coverage; }
854

855
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
856
  {
857
    unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
B
Behdad Esfahbod 已提交
858
    if (likely (index == NOT_COVERED)) return false;
859 860

    const LigatureSet &lig_set = this+ligatureSet[index];
B
Behdad Esfahbod 已提交
861
    return lig_set.would_apply (c);
B
Behdad Esfahbod 已提交
862 863
  }

864
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
865
  {
B
Behdad Esfahbod 已提交
866
    TRACE_APPLY (this);
867

868
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
B
Behdad Esfahbod 已提交
869
    if (likely (index == NOT_COVERED)) return_trace (false);
870

B
Behdad Esfahbod 已提交
871
    const LigatureSet &lig_set = this+ligatureSet[index];
B
Behdad Esfahbod 已提交
872
    return_trace (lig_set.apply (c));
873
  }
B
Behdad Esfahbod 已提交
874

875
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
876
		  hb_sorted_array_t<const GlyphID> first_glyphs,
B
Behdad Esfahbod 已提交
877 878 879 880
		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
		  hb_array_t<const GlyphID> ligatures_list,
		  hb_array_t<const unsigned int> component_count_list,
		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
881
  {
B
Behdad Esfahbod 已提交
882
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
883
    if (unlikely (!c->extend_min (*this))) return_trace (false);
884 885
    if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
    for (unsigned int i = 0; i < first_glyphs.length; i++)
B
Behdad Esfahbod 已提交
886 887 888 889 890 891 892 893 894 895 896
    {
      unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
      if (unlikely (!ligatureSet[i].serialize (c, this)
				   .serialize (c,
					       ligatures_list.sub_array (0, ligature_count),
					       component_count_list.sub_array (0, ligature_count),
					       component_list))) return_trace (false);
      ligatures_list += ligature_count;
      component_count_list += ligature_count;
    }
    return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
897 898
  }

899
  bool subset (hb_subset_context_t *c) const
900 901 902 903 904 905
  {
    TRACE_SUBSET (this);
    // TODO(subset)
    return_trace (false);
  }

906
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
907
  {
B
Behdad Esfahbod 已提交
908
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
909
    return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
B
Behdad Esfahbod 已提交
910 911
  }

912
  protected:
B
Behdad Esfahbod 已提交
913
  HBUINT16	format;			/* Format identifier--format = 1 */
B
Behdad Esfahbod 已提交
914 915
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
916
					 * beginning of Substitution table */
B
Behdad Esfahbod 已提交
917
  OffsetArrayOf<LigatureSet>
B
Behdad Esfahbod 已提交
918 919
		ligatureSet;		/* Array LigatureSet tables
					 * ordered by Coverage Index */
920
  public:
921
  DEFINE_SIZE_ARRAY (6, ligatureSet);
B
Behdad Esfahbod 已提交
922
};
923

B
Behdad Esfahbod 已提交
924 925
struct LigatureSubst
{
926
  bool serialize (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
927
		  hb_sorted_array_t<const GlyphID> first_glyphs,
B
Behdad Esfahbod 已提交
928 929 930 931
		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
		  hb_array_t<const GlyphID> ligatures_list,
		  hb_array_t<const unsigned int> component_count_list,
		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
932
  {
B
Behdad Esfahbod 已提交
933
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
934
    if (unlikely (!c->extend_min (u.format))) return_trace (false);
935
    unsigned int format = 1;
936
    u.format = format;
937
    switch (u.format) {
B
Behdad Esfahbod 已提交
938 939 940 941 942 943 944
    case 1: return_trace (u.format1.serialize (c,
					       first_glyphs,
					       ligature_per_first_glyph_count_list,
					       ligatures_list,
					       component_count_list,
					       component_list));
    default:return_trace (false);
945 946 947
    }
  }

948
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
949
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
B
Behdad Esfahbod 已提交
950
  {
951
    TRACE_DISPATCH (this, u.format);
952
    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
B
Behdad Esfahbod 已提交
953
    switch (u.format) {
954
    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
955
    default:return_trace (c->default_return_value ());
B
Behdad Esfahbod 已提交
956 957 958
    }
  }

959
  protected:
960
  union {
B
Behdad Esfahbod 已提交
961
  HBUINT16		format;		/* Format identifier */
B
Behdad Esfahbod 已提交
962
  LigatureSubstFormat1	format1;
963 964 965
  } u;
};

B
Behdad Esfahbod 已提交
966

B
Minor  
Behdad Esfahbod 已提交
967
struct ContextSubst : Context {};
B
Behdad Esfahbod 已提交
968

B
Minor  
Behdad Esfahbod 已提交
969
struct ChainContextSubst : ChainContext {};
970

B
Behdad Esfahbod 已提交
971
struct ExtensionSubst : Extension<ExtensionSubst>
B
Behdad Esfahbod 已提交
972
{
B
Behdad Esfahbod 已提交
973
  typedef struct SubstLookupSubTable SubTable;
B
Behdad Esfahbod 已提交
974

975
  bool is_reverse () const;
976 977 978
};


B
Behdad Esfahbod 已提交
979 980
struct ReverseChainSingleSubstFormat1
{
981
  bool intersects (const hb_set_t *glyphs) const
982 983 984 985
  {
    if (!(this+coverage).intersects (glyphs))
      return false;

986
    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002

    unsigned int count;

    count = backtrack.len;
    for (unsigned int i = 0; i < count; i++)
      if (!(this+backtrack[i]).intersects (glyphs))
        return false;

    count = lookahead.len;
    for (unsigned int i = 0; i < count; i++)
      if (!(this+lookahead[i]).intersects (glyphs))
        return false;

    return true;
  }

1003
  void closure (hb_closure_context_t *c) const
1004
  {
B
Behdad Esfahbod 已提交
1005
    if (!intersects (c->glyphs)) return;
B
Behdad Esfahbod 已提交
1006

1007 1008
    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
B
Behdad Esfahbod 已提交
1009

B
Behdad Esfahbod 已提交
1010 1011 1012
    + hb_zip (this+coverage, substitute)
    | hb_filter (*c->glyphs, hb_first)
    | hb_map (hb_second)
1013
    | hb_sink (c->output)
1014
    ;
1015 1016
  }

1017
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
1018
  {
1019
    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
1020 1021 1022 1023 1024

    unsigned int count;

    count = backtrack.len;
    for (unsigned int i = 0; i < count; i++)
1025
      if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return;
1026

1027
    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
1028 1029
    count = lookahead.len;
    for (unsigned int i = 0; i < count; i++)
1030
      if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return;
1031

1032
    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
1033
    count = substitute.len;
1034
    c->output->add_array (substitute.arrayZ, substitute.len);
1035 1036
  }

1037
  const Coverage &get_coverage () const { return this+coverage; }
1038

1039
  bool would_apply (hb_would_apply_context_t *c) const
B
Behdad Esfahbod 已提交
1040
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
1041

1042
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
1043
  {
B
Behdad Esfahbod 已提交
1044
    TRACE_APPLY (this);
1045
    if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
B
Behdad Esfahbod 已提交
1046
      return_trace (false); /* No chaining to this type */
1047

1048
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
B
Behdad Esfahbod 已提交
1049
    if (likely (index == NOT_COVERED)) return_trace (false);
1050

1051 1052
    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
1053

1054
  unsigned int start_index = 0, end_index = 0;
B
Behdad Esfahbod 已提交
1055
    if (match_backtrack (c,
1056
			 backtrack.len, (HBUINT16 *) backtrack.arrayZ,
1057 1058
			 match_coverage, this,
			 &start_index) &&
B
Behdad Esfahbod 已提交
1059
        match_lookahead (c,
1060
			 lookahead.len, (HBUINT16 *) lookahead.arrayZ,
B
Behdad Esfahbod 已提交
1061
			 match_coverage, this,
1062
			 1, &end_index))
1063
    {
1064
      c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
B
Behdad Esfahbod 已提交
1065
      c->replace_glyph_inplace (substitute[index]);
1066 1067 1068
      /* Note: We DON'T decrease buffer->idx.  The main loop does it
       * for us.  This is useful for preventing surprises if someone
       * calls us through a Context lookup. */
B
Behdad Esfahbod 已提交
1069
      return_trace (true);
1070 1071
    }

B
Behdad Esfahbod 已提交
1072
    return_trace (false);
1073
  }
B
Behdad Esfahbod 已提交
1074

1075
  bool subset (hb_subset_context_t *c) const
1076 1077 1078 1079 1080 1081
  {
    TRACE_SUBSET (this);
    // TODO(subset)
    return_trace (false);
  }

1082
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
1083
  {
B
Behdad Esfahbod 已提交
1084
    TRACE_SANITIZE (this);
1085
    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
B
Behdad Esfahbod 已提交
1086
      return_trace (false);
1087
    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
B
Behdad Esfahbod 已提交
1088
    if (!lookahead.sanitize (c, this))
B
Behdad Esfahbod 已提交
1089
      return_trace (false);
1090
    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
B
Behdad Esfahbod 已提交
1091
    return_trace (substitute.sanitize (c));
B
Behdad Esfahbod 已提交
1092 1093
  }

1094
  protected:
B
Behdad Esfahbod 已提交
1095
  HBUINT16	format;			/* Format identifier--format = 1 */
1096 1097 1098 1099 1100
  OffsetTo<Coverage>
		coverage;		/* Offset to Coverage table--from
					 * beginning of table */
  OffsetArrayOf<Coverage>
		backtrack;		/* Array of coverage tables
1101
					 * in backtracking sequence, in glyph
B
Behdad Esfahbod 已提交
1102
					 * sequence order */
1103 1104 1105
  OffsetArrayOf<Coverage>
		lookaheadX;		/* Array of coverage tables
					 * in lookahead sequence, in glyph
B
Behdad Esfahbod 已提交
1106
					 * sequence order */
1107 1108 1109
  ArrayOf<GlyphID>
		substituteX;		/* Array of substitute
					 * GlyphIDs--ordered by Coverage Index */
1110
  public:
B
Behdad Esfahbod 已提交
1111
  DEFINE_SIZE_MIN (10);
B
Behdad Esfahbod 已提交
1112 1113
};

B
Behdad Esfahbod 已提交
1114 1115
struct ReverseChainSingleSubst
{
1116
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
1117
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1118
  {
1119
    TRACE_DISPATCH (this, u.format);
1120
    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
1121
    switch (u.format) {
1122
    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
1123
    default:return_trace (c->default_return_value ());
1124 1125 1126
    }
  }

1127
  protected:
1128
  union {
B
Behdad Esfahbod 已提交
1129
  HBUINT16				format;		/* Format identifier */
B
Behdad Esfahbod 已提交
1130
  ReverseChainSingleSubstFormat1	format1;
1131 1132 1133 1134 1135
  } u;
};



B
Behdad Esfahbod 已提交
1136 1137 1138 1139
/*
 * SubstLookup
 */

B
Behdad Esfahbod 已提交
1140 1141
struct SubstLookupSubTable
{
B
Behdad Esfahbod 已提交
1142
  friend struct Lookup;
B
Behdad Esfahbod 已提交
1143 1144
  friend struct SubstLookup;

B
Behdad Esfahbod 已提交
1145
  enum Type {
1146 1147 1148 1149 1150 1151 1152
    Single		= 1,
    Multiple		= 2,
    Alternate		= 3,
    Ligature		= 4,
    Context		= 5,
    ChainContext	= 6,
    Extension		= 7,
1153
    ReverseChainSingle	= 8
1154 1155
  };

1156
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
1157
  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
1158
  {
1159
    TRACE_DISPATCH (this, lookup_type);
1160
    switch (lookup_type) {
1161 1162 1163 1164 1165 1166 1167 1168
    case Single:		return_trace (u.single.dispatch (c, hb_forward<Ts> (ds)...));
    case Multiple:		return_trace (u.multiple.dispatch (c, hb_forward<Ts> (ds)...));
    case Alternate:		return_trace (u.alternate.dispatch (c, hb_forward<Ts> (ds)...));
    case Ligature:		return_trace (u.ligature.dispatch (c, hb_forward<Ts> (ds)...));
    case Context:		return_trace (u.context.dispatch (c, hb_forward<Ts> (ds)...));
    case ChainContext:		return_trace (u.chainContext.dispatch (c, hb_forward<Ts> (ds)...));
    case Extension:		return_trace (u.extension.dispatch (c, hb_forward<Ts> (ds)...));
    case ReverseChainSingle:	return_trace (u.reverseChainContextSingle.dispatch (c, hb_forward<Ts> (ds)...));
B
Behdad Esfahbod 已提交
1169
    default:			return_trace (c->default_return_value ());
1170 1171 1172
    }
  }

1173
  protected:
B
Behdad Esfahbod 已提交
1174
  union {
B
Behdad Esfahbod 已提交
1175 1176 1177 1178
  SingleSubst			single;
  MultipleSubst			multiple;
  AlternateSubst		alternate;
  LigatureSubst			ligature;
1179
  ContextSubst			context;
B
Behdad Esfahbod 已提交
1180 1181 1182
  ChainContextSubst		chainContext;
  ExtensionSubst		extension;
  ReverseChainSingleSubst	reverseChainContextSingle;
B
Behdad Esfahbod 已提交
1183
  } u;
B
Behdad Esfahbod 已提交
1184
  public:
B
Behdad Esfahbod 已提交
1185
  DEFINE_SIZE_MIN (0);
B
Behdad Esfahbod 已提交
1186 1187
};

1188

B
Behdad Esfahbod 已提交
1189 1190
struct SubstLookup : Lookup
{
B
Behdad Esfahbod 已提交
1191 1192
  typedef SubstLookupSubTable SubTable;

1193
  const SubTable& get_subtable (unsigned int i) const
B
Behdad Esfahbod 已提交
1194
  { return Lookup::get_subtable<SubTable> (i); }
B
Behdad Esfahbod 已提交
1195

B
Behdad Esfahbod 已提交
1196
  HB_INTERNAL static bool lookup_type_is_reverse (unsigned int lookup_type)
B
Behdad Esfahbod 已提交
1197
  { return lookup_type == SubTable::ReverseChainSingle; }
B
Behdad Esfahbod 已提交
1198

1199
  bool is_reverse () const
B
Behdad Esfahbod 已提交
1200
  {
B
Behdad Esfahbod 已提交
1201
    unsigned int type = get_type ();
B
Behdad Esfahbod 已提交
1202
    if (unlikely (type == SubTable::Extension))
1203
      return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
B
Behdad Esfahbod 已提交
1204
    return lookup_type_is_reverse (type);
B
Behdad Esfahbod 已提交
1205
  }
1206

1207
  bool apply (hb_ot_apply_context_t *c) const
B
Behdad Esfahbod 已提交
1208 1209
  {
    TRACE_APPLY (this);
B
Behdad Esfahbod 已提交
1210
    return_trace (dispatch (c));
B
Behdad Esfahbod 已提交
1211 1212
  }

1213
  bool intersects (const hb_set_t *glyphs) const
1214 1215 1216 1217 1218
  {
    hb_intersects_context_t c (glyphs);
    return dispatch (&c);
  }

1219
  hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
1220
  {
1221
    if (!c->should_visit_lookup (this_index))
B
Behdad Esfahbod 已提交
1222
      return hb_closure_context_t::default_return_value ();
1223 1224

    c->set_recurse_func (dispatch_closure_recurse_func);
1225 1226 1227 1228 1229

    hb_closure_context_t::return_t ret = dispatch (c);

    c->flush ();

B
Behdad Esfahbod 已提交
1230
    return ret;
B
Behdad Esfahbod 已提交
1231 1232
  }

1233
  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
B
Behdad Esfahbod 已提交
1234
  {
1235
    c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
B
Behdad Esfahbod 已提交
1236
    return dispatch (c);
1237 1238
  }

B
Behdad Esfahbod 已提交
1239
  template <typename set_t>
1240
  void add_coverage (set_t *glyphs) const
B
Behdad Esfahbod 已提交
1241
  {
1242 1243
    hb_add_coverage_context_t<set_t> c (glyphs);
    dispatch (&c);
B
Behdad Esfahbod 已提交
1244 1245
  }

1246 1247
  bool would_apply (hb_would_apply_context_t *c,
		    const hb_ot_layout_lookup_accelerator_t *accel) const
B
Behdad Esfahbod 已提交
1248
  {
B
Behdad Esfahbod 已提交
1249 1250 1251
    if (unlikely (!c->len)) return false;
    if (!accel->may_have (c->glyphs[0])) return false;
      return dispatch (c);
B
Behdad Esfahbod 已提交
1252 1253
  }

B
Behdad Esfahbod 已提交
1254
  HB_INTERNAL static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
B
Behdad Esfahbod 已提交
1255

1256
  SubTable& serialize_subtable (hb_serialize_context_t *c,
B
Behdad Esfahbod 已提交
1257 1258
				       unsigned int i)
  { return get_subtables<SubTable> ()[i].serialize (c, this); }
1259

1260 1261
  bool serialize_single (hb_serialize_context_t *c,
			 uint32_t lookup_props,
B
Behdad Esfahbod 已提交
1262
		         hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
1263
		         hb_array_t<const GlyphID> substitutes)
1264
  {
B
Behdad Esfahbod 已提交
1265
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
1266
    if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
B
Behdad Esfahbod 已提交
1267
    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes));
1268 1269
  }

1270 1271
  bool serialize_multiple (hb_serialize_context_t *c,
			   uint32_t lookup_props,
B
Behdad Esfahbod 已提交
1272
			   hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
1273 1274
			   hb_array_t<const unsigned int> substitute_len_list,
			   hb_array_t<const GlyphID> substitute_glyphs_list)
1275
  {
B
Behdad Esfahbod 已提交
1276
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
1277
    if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
B
Behdad Esfahbod 已提交
1278 1279 1280 1281
    return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
								  glyphs,
								  substitute_len_list,
								  substitute_glyphs_list));
1282 1283
  }

1284 1285
  bool serialize_alternate (hb_serialize_context_t *c,
			    uint32_t lookup_props,
B
Behdad Esfahbod 已提交
1286
			    hb_sorted_array_t<const GlyphID> glyphs,
B
Behdad Esfahbod 已提交
1287 1288
			    hb_array_t<const unsigned int> alternate_len_list,
			    hb_array_t<const GlyphID> alternate_glyphs_list)
1289
  {
B
Behdad Esfahbod 已提交
1290
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
1291
    if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
B
Behdad Esfahbod 已提交
1292 1293 1294 1295
    return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
								   glyphs,
								   alternate_len_list,
								   alternate_glyphs_list));
1296 1297
  }

1298 1299
  bool serialize_ligature (hb_serialize_context_t *c,
			   uint32_t lookup_props,
B
Behdad Esfahbod 已提交
1300
			   hb_sorted_array_t<const GlyphID> first_glyphs,
B
Behdad Esfahbod 已提交
1301 1302 1303 1304
			   hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
			   hb_array_t<const GlyphID> ligatures_list,
			   hb_array_t<const unsigned int> component_count_list,
			   hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
1305
  {
B
Behdad Esfahbod 已提交
1306
    TRACE_SERIALIZE (this);
B
Behdad Esfahbod 已提交
1307
    if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
B
Behdad Esfahbod 已提交
1308 1309 1310 1311 1312 1313
    return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
								  first_glyphs,
								  ligature_per_first_glyph_count_list,
								  ligatures_list,
								  component_count_list,
								  component_list));
1314 1315
  }

B
Behdad Esfahbod 已提交
1316
  template <typename context_t>
B
Behdad Esfahbod 已提交
1317
  HB_INTERNAL static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
B
Behdad Esfahbod 已提交
1318

B
Behdad Esfahbod 已提交
1319
  HB_INTERNAL static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
1320
  {
1321
    if (!c->should_visit_lookup (lookup_index))
1322
      return hb_void_t ();
1323 1324 1325

    hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);

1326 1327 1328 1329
    /* While in theory we should flush here, it will cause timeouts because a recursive
     * lookup can keep growing the glyph set.  Skip, and outer loop will retry up to
     * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
    //c->flush ();
1330 1331

    return ret;
1332 1333
  }

1334
  template <typename context_t, typename ...Ts>
B
Behdad Esfahbod 已提交
1335
  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
1336
  { return Lookup::dispatch<SubTable> (c, hb_forward<Ts> (ds)...); }
B
Behdad Esfahbod 已提交
1337

1338
  bool subset (hb_subset_context_t *c) const
1339
  { return Lookup::subset<SubTable> (c); }
B
Behdad Esfahbod 已提交
1340

1341
  bool sanitize (hb_sanitize_context_t *c) const
B
Behdad Esfahbod 已提交
1342
  { return Lookup::sanitize<SubTable> (c); }
B
Behdad Esfahbod 已提交
1343 1344
};

B
Minor  
Behdad Esfahbod 已提交
1345
/*
1346 1347
 * GSUB -- Glyph Substitution
 * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
B
Minor  
Behdad Esfahbod 已提交
1348 1349
 */

B
Behdad Esfahbod 已提交
1350 1351
struct GSUB : GSUBGPOS
{
1352
  static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
B
Minor  
Behdad Esfahbod 已提交
1353

1354
  const SubstLookup& get_lookup (unsigned int i) const
1355
  { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
B
Behdad Esfahbod 已提交
1356

1357
  bool subset (hb_subset_context_t *c) const
1358
  { return GSUBGPOS::subset<SubstLookup> (c); }
1359

1360
  bool sanitize (hb_sanitize_context_t *c) const
1361
  { return GSUBGPOS::sanitize<SubstLookup> (c); }
B
WIP  
Behdad Esfahbod 已提交
1362

1363 1364 1365
  HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
				   hb_face_t *face) const;

B
WIP  
Behdad Esfahbod 已提交
1366
  typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
B
Minor  
Behdad Esfahbod 已提交
1367
};
1368 1369


B
Behdad Esfahbod 已提交
1370 1371 1372
struct GSUB_accelerator_t : GSUB::accelerator_t {};


B
Behdad Esfahbod 已提交
1373
/* Out-of-class implementation for methods recursing */
1374

1375
/*static*/ inline bool ExtensionSubst::is_reverse () const
B
Behdad Esfahbod 已提交
1376 1377
{
  unsigned int type = get_type ();
B
Behdad Esfahbod 已提交
1378 1379
  if (unlikely (type == SubTable::Extension))
    return CastR<ExtensionSubst> (get_subtable<SubTable>()).is_reverse ();
B
Behdad Esfahbod 已提交
1380 1381 1382
  return SubstLookup::lookup_type_is_reverse (type);
}

1383
template <typename context_t>
1384
/*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
1385
{
B
Behdad Esfahbod 已提交
1386
  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1387
  return l.dispatch (c);
1388 1389
}

1390
/*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
B
Behdad Esfahbod 已提交
1391
{
B
Behdad Esfahbod 已提交
1392
  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
1393
  unsigned int saved_lookup_props = c->lookup_props;
1394 1395 1396
  unsigned int saved_lookup_index = c->lookup_index;
  c->set_lookup_index (lookup_index);
  c->set_lookup_props (l.get_props ());
1397
  bool ret = l.dispatch (c);
1398
  c->set_lookup_index (saved_lookup_index);
1399
  c->set_lookup_props (saved_lookup_props);
1400
  return ret;
1401 1402
}

B
Behdad Esfahbod 已提交
1403
} /* namespace OT */
1404

B
Behdad Esfahbod 已提交
1405

1406
#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */