~ubuntu-branches/ubuntu/trusty/pango1.0/trusty-proposed

« back to all changes in this revision

Viewing changes to pango/opentype/hb-ot-layout-gsub-private.hh

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2009-09-09 07:49:30 UTC
  • mfrom: (1.6.1 upstream) (63.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090909074930-zlgadwz6svi311vh
Tags: 1.25.6-1
New upstream development release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007,2008,2009  Red Hat, Inc.
 
3
 *
 
4
 *  This is part of HarfBuzz, an OpenType Layout engine library.
 
5
 *
 
6
 * Permission is hereby granted, without written agreement and without
 
7
 * license or royalty fees, to use, copy, modify, and distribute this
 
8
 * software and its documentation for any purpose, provided that the
 
9
 * above copyright notice and the following two paragraphs appear in
 
10
 * all copies of this software.
 
11
 *
 
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 
16
 * DAMAGE.
 
17
 *
 
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
23
 *
 
24
 * Red Hat Author(s): Behdad Esfahbod
 
25
 */
 
26
 
 
27
#ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
 
28
#define HB_OT_LAYOUT_GSUB_PRIVATE_HH
 
29
 
 
30
#include "hb-ot-layout-gsubgpos-private.hh"
 
31
 
 
32
 
 
33
struct SingleSubstFormat1
 
34
{
 
35
  friend struct SingleSubst;
 
36
 
 
37
  private:
 
38
 
 
39
  inline bool apply (APPLY_ARG_DEF) const
 
40
  {
 
41
    TRACE_APPLY ();
 
42
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
 
43
    unsigned int index = (this+coverage) (glyph_id);
 
44
    if (HB_LIKELY (index == NOT_COVERED))
 
45
      return false;
 
46
 
 
47
    glyph_id += deltaGlyphID;
 
48
    _hb_buffer_replace_glyph (buffer, glyph_id);
 
49
 
 
50
    /* We inherit the old glyph class to the substituted glyph */
 
51
    if (_hb_ot_layout_has_new_glyph_classes (context->face))
 
52
      _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
 
53
 
 
54
    return true;
 
55
  }
 
56
 
 
57
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
58
    TRACE_SANITIZE ();
 
59
    return SANITIZE_THIS (coverage) && SANITIZE (deltaGlyphID);
 
60
  }
 
61
 
 
62
  private:
 
63
  USHORT        format;                 /* Format identifier--format = 1 */
 
64
  OffsetTo<Coverage>
 
65
                coverage;               /* Offset to Coverage table--from
 
66
                                         * beginning of Substitution table */
 
67
  SHORT         deltaGlyphID;           /* Add to original GlyphID to get
 
68
                                         * substitute GlyphID */
 
69
};
 
70
ASSERT_SIZE (SingleSubstFormat1, 6);
 
71
 
 
72
struct SingleSubstFormat2
 
73
{
 
74
  friend struct SingleSubst;
 
75
 
 
76
  private:
 
77
 
 
78
  inline bool apply (APPLY_ARG_DEF) const
 
79
  {
 
80
    TRACE_APPLY ();
 
81
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
 
82
    unsigned int index = (this+coverage) (glyph_id);
 
83
    if (HB_LIKELY (index == NOT_COVERED))
 
84
      return false;
 
85
 
 
86
    if (HB_UNLIKELY (index >= substitute.len))
 
87
      return false;
 
88
 
 
89
    glyph_id = substitute[index];
 
90
    _hb_buffer_replace_glyph (buffer, glyph_id);
 
91
 
 
92
    /* We inherit the old glyph class to the substituted glyph */
 
93
    if (_hb_ot_layout_has_new_glyph_classes (context->face))
 
94
      _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
 
95
 
 
96
    return true;
 
97
  }
 
98
 
 
99
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
100
    TRACE_SANITIZE ();
 
101
    return SANITIZE_THIS (coverage) && SANITIZE (substitute);
 
102
  }
 
103
 
 
104
  private:
 
105
  USHORT        format;                 /* Format identifier--format = 2 */
 
106
  OffsetTo<Coverage>
 
107
                coverage;               /* Offset to Coverage table--from
 
108
                                         * beginning of Substitution table */
 
109
  ArrayOf<GlyphID>
 
110
                substitute;             /* Array of substitute
 
111
                                         * GlyphIDs--ordered by Coverage Index */
 
112
};
 
113
ASSERT_SIZE (SingleSubstFormat2, 6);
 
114
 
 
115
struct SingleSubst
 
116
{
 
117
  friend struct SubstLookupSubTable;
 
118
 
 
119
  private:
 
120
 
 
121
  inline bool apply (APPLY_ARG_DEF) const
 
122
  {
 
123
    TRACE_APPLY ();
 
124
    switch (u.format) {
 
125
    case 1: return u.format1->apply (APPLY_ARG);
 
126
    case 2: return u.format2->apply (APPLY_ARG);
 
127
    default:return false;
 
128
    }
 
129
  }
 
130
 
 
131
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
132
    TRACE_SANITIZE ();
 
133
    if (!SANITIZE (u.format)) return false;
 
134
    switch (u.format) {
 
135
    case 1: return u.format1->sanitize (SANITIZE_ARG);
 
136
    case 2: return u.format2->sanitize (SANITIZE_ARG);
 
137
    default:return true;
 
138
    }
 
139
  }
 
140
 
 
141
  private:
 
142
  union {
 
143
  USHORT                format;         /* Format identifier */
 
144
  SingleSubstFormat1    format1[];
 
145
  SingleSubstFormat2    format2[];
 
146
  } u;
 
147
};
 
148
ASSERT_SIZE (SingleSubst, 2);
 
149
 
 
150
 
 
151
struct Sequence
 
152
{
 
153
  friend struct MultipleSubstFormat1;
 
154
 
 
155
  private:
 
156
  inline bool apply (APPLY_ARG_DEF) const
 
157
  {
 
158
    TRACE_APPLY ();
 
159
    if (HB_UNLIKELY (!substitute.len))
 
160
      return false;
 
161
 
 
162
    _hb_buffer_add_output_glyphs (buffer, 1,
 
163
                                  substitute.len, (const uint16_t *) substitute.array,
 
164
                                  0xFFFF, 0xFFFF);
 
165
 
 
166
    /* This is a guess only ... */
 
167
    if (_hb_ot_layout_has_new_glyph_classes (context->face))
 
168
    {
 
169
      if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
 
170
        property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
 
171
 
 
172
      unsigned int count = substitute.len;
 
173
      for (unsigned int n = 0; n < count; n++)
 
174
        _hb_ot_layout_set_glyph_property (context->face, substitute[n], property);
 
175
    }
 
176
 
 
177
    return true;
 
178
  }
 
179
 
 
180
  public:
 
181
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
182
    TRACE_SANITIZE ();
 
183
    return SANITIZE (substitute);
 
184
  }
 
185
 
 
186
  private:
 
187
  ArrayOf<GlyphID>
 
188
                substitute;             /* String of GlyphIDs to substitute */
 
189
};
 
190
ASSERT_SIZE (Sequence, 2);
 
191
 
 
192
struct MultipleSubstFormat1
 
193
{
 
194
  friend struct MultipleSubst;
 
195
 
 
196
  private:
 
197
 
 
198
  inline bool apply (APPLY_ARG_DEF) const
 
199
  {
 
200
    TRACE_APPLY ();
 
201
 
 
202
    unsigned int index = (this+coverage) (IN_CURGLYPH ());
 
203
    if (HB_LIKELY (index == NOT_COVERED))
 
204
      return false;
 
205
 
 
206
    return (this+sequence[index]).apply (APPLY_ARG);
 
207
  }
 
208
 
 
209
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
210
    TRACE_SANITIZE ();
 
211
    return SANITIZE_THIS2 (coverage, sequence);
 
212
  }
 
213
 
 
214
  private:
 
215
  USHORT        format;                 /* Format identifier--format = 1 */
 
216
  OffsetTo<Coverage>
 
217
                coverage;               /* Offset to Coverage table--from
 
218
                                         * beginning of Substitution table */
 
219
  OffsetArrayOf<Sequence>
 
220
                sequence;               /* Array of Sequence tables
 
221
                                         * ordered by Coverage Index */
 
222
};
 
223
ASSERT_SIZE (MultipleSubstFormat1, 6);
 
224
 
 
225
struct MultipleSubst
 
226
{
 
227
  friend struct SubstLookupSubTable;
 
228
 
 
229
  private:
 
230
 
 
231
  inline bool apply (APPLY_ARG_DEF) const
 
232
  {
 
233
    TRACE_APPLY ();
 
234
    switch (u.format) {
 
235
    case 1: return u.format1->apply (APPLY_ARG);
 
236
    default:return false;
 
237
    }
 
238
  }
 
239
 
 
240
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
241
    TRACE_SANITIZE ();
 
242
    if (!SANITIZE (u.format)) return false;
 
243
    switch (u.format) {
 
244
    case 1: return u.format1->sanitize (SANITIZE_ARG);
 
245
    default:return true;
 
246
    }
 
247
  }
 
248
 
 
249
  private:
 
250
  union {
 
251
  USHORT                format;         /* Format identifier */
 
252
  MultipleSubstFormat1  format1[];
 
253
  } u;
 
254
};
 
255
ASSERT_SIZE (MultipleSubst, 2);
 
256
 
 
257
 
 
258
typedef ArrayOf<GlyphID> AlternateSet;  /* Array of alternate GlyphIDs--in
 
259
                                         * arbitrary order */
 
260
ASSERT_SIZE (AlternateSet, 2);
 
261
 
 
262
struct AlternateSubstFormat1
 
263
{
 
264
  friend struct AlternateSubst;
 
265
 
 
266
  private:
 
267
 
 
268
  inline bool apply (APPLY_ARG_DEF) const
 
269
  {
 
270
    TRACE_APPLY ();
 
271
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
 
272
 
 
273
    unsigned int index = (this+coverage) (glyph_id);
 
274
    if (HB_LIKELY (index == NOT_COVERED))
 
275
      return false;
 
276
 
 
277
    const AlternateSet &alt_set = this+alternateSet[index];
 
278
 
 
279
    if (HB_UNLIKELY (!alt_set.len))
 
280
      return false;
 
281
 
 
282
    unsigned int alt_index = 0;
 
283
 
 
284
    /* XXX callback to user to choose alternate
 
285
    if (context->face->altfunc)
 
286
      alt_index = (context->face->altfunc)(context->layout, buffer,
 
287
                                    buffer->out_pos, glyph_id,
 
288
                                    alt_set.len, alt_set.array);
 
289
                                   */
 
290
 
 
291
    if (HB_UNLIKELY (alt_index >= alt_set.len))
 
292
      return false;
 
293
 
 
294
    glyph_id = alt_set[alt_index];
 
295
 
 
296
    _hb_buffer_replace_glyph (buffer, glyph_id);
 
297
 
 
298
    /* We inherit the old glyph class to the substituted glyph */
 
299
    if (_hb_ot_layout_has_new_glyph_classes (context->face))
 
300
      _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
 
301
 
 
302
    return true;
 
303
  }
 
304
 
 
305
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
306
    TRACE_SANITIZE ();
 
307
    return SANITIZE_THIS2 (coverage, alternateSet);
 
308
  }
 
309
 
 
310
  private:
 
311
  USHORT        format;                 /* Format identifier--format = 1 */
 
312
  OffsetTo<Coverage>
 
313
                coverage;               /* Offset to Coverage table--from
 
314
                                         * beginning of Substitution table */
 
315
  OffsetArrayOf<AlternateSet>
 
316
                alternateSet;           /* Array of AlternateSet tables
 
317
                                         * ordered by Coverage Index */
 
318
};
 
319
ASSERT_SIZE (AlternateSubstFormat1, 6);
 
320
 
 
321
struct AlternateSubst
 
322
{
 
323
  friend struct SubstLookupSubTable;
 
324
 
 
325
  private:
 
326
 
 
327
  inline bool apply (APPLY_ARG_DEF) const
 
328
  {
 
329
    TRACE_APPLY ();
 
330
    switch (u.format) {
 
331
    case 1: return u.format1->apply (APPLY_ARG);
 
332
    default:return false;
 
333
    }
 
334
  }
 
335
 
 
336
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
337
    TRACE_SANITIZE ();
 
338
    if (!SANITIZE (u.format)) return false;
 
339
    switch (u.format) {
 
340
    case 1: return u.format1->sanitize (SANITIZE_ARG);
 
341
    default:return true;
 
342
    }
 
343
  }
 
344
 
 
345
  private:
 
346
  union {
 
347
  USHORT                format;         /* Format identifier */
 
348
  AlternateSubstFormat1 format1[];
 
349
  } u;
 
350
};
 
351
ASSERT_SIZE (AlternateSubst, 2);
 
352
 
 
353
 
 
354
struct Ligature
 
355
{
 
356
  friend struct LigatureSet;
 
357
 
 
358
  private:
 
359
  inline bool apply (APPLY_ARG_DEF, bool is_mark) const
 
360
  {
 
361
    TRACE_APPLY ();
 
362
    unsigned int i, j;
 
363
    unsigned int count = component.len;
 
364
    unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
 
365
    if (HB_UNLIKELY (buffer->in_pos + count > end))
 
366
      return false;
 
367
 
 
368
    for (i = 1, j = buffer->in_pos + 1; i < count; i++, j++)
 
369
    {
 
370
      while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property))
 
371
      {
 
372
        if (HB_UNLIKELY (j + count - i == end))
 
373
          return false;
 
374
        j++;
 
375
      }
 
376
 
 
377
      if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
 
378
        is_mark = FALSE;
 
379
 
 
380
      if (HB_LIKELY (IN_GLYPH (j) != component[i]))
 
381
        return false;
 
382
    }
 
383
    /* This is just a guess ... */
 
384
    if (_hb_ot_layout_has_new_glyph_classes (context->face))
 
385
      _hb_ot_layout_set_glyph_class (context->face, ligGlyph,
 
386
                                     is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
 
387
                                             : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
 
388
 
 
389
    if (j == buffer->in_pos + i) /* No input glyphs skipped */
 
390
      /* We don't use a new ligature ID if there are no skipped
 
391
         glyphs and the ligature already has an ID. */
 
392
      _hb_buffer_add_output_glyphs (buffer, i,
 
393
                                    1, (const uint16_t *) &ligGlyph,
 
394
                                    0xFFFF,
 
395
                                    IN_LIGID (buffer->in_pos) ?
 
396
                                    0xFFFF : _hb_buffer_allocate_lig_id (buffer));
 
397
    else
 
398
    {
 
399
      unsigned int lig_id = _hb_buffer_allocate_lig_id (buffer);
 
400
      _hb_buffer_add_output_glyph (buffer, ligGlyph, 0xFFFF, lig_id);
 
401
 
 
402
      /* Now we must do a second loop to copy the skipped glyphs to
 
403
         `out' and assign component values to it.  We start with the
 
404
         glyph after the first component.  Glyphs between component
 
405
         i and i+1 belong to component i.  Together with the lig_id
 
406
         value it is later possible to check whether a specific
 
407
         component value really belongs to a given ligature. */
 
408
 
 
409
      for ( i = 1; i < count; i++ )
 
410
      {
 
411
        while (_hb_ot_layout_skip_mark (context->face, IN_CURINFO (), lookup_flag, NULL))
 
412
          _hb_buffer_add_output_glyph (buffer, IN_CURGLYPH (), i - 1, lig_id);
 
413
 
 
414
        (buffer->in_pos)++;
 
415
      }
 
416
 
 
417
      /* TODO We should possibly reassign lig_id and component for any
 
418
       * components of a previous ligature that s now being removed as part of
 
419
       * this ligature. */
 
420
    }
 
421
 
 
422
    return true;
 
423
  }
 
424
 
 
425
  public:
 
426
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
427
    TRACE_SANITIZE ();
 
428
    return SANITIZE2 (ligGlyph, component);
 
429
  }
 
430
 
 
431
  private:
 
432
  GlyphID       ligGlyph;               /* GlyphID of ligature to substitute */
 
433
  HeadlessArrayOf<GlyphID>
 
434
                component;              /* Array of component GlyphIDs--start
 
435
                                         * with the second  component--ordered
 
436
                                         * in writing direction */
 
437
};
 
438
ASSERT_SIZE (Ligature, 4);
 
439
 
 
440
struct LigatureSet
 
441
{
 
442
  friend struct LigatureSubstFormat1;
 
443
 
 
444
  private:
 
445
  inline bool apply (APPLY_ARG_DEF, bool is_mark) const
 
446
  {
 
447
    TRACE_APPLY ();
 
448
    unsigned int num_ligs = ligature.len;
 
449
    for (unsigned int i = 0; i < num_ligs; i++)
 
450
    {
 
451
      const Ligature &lig = this+ligature[i];
 
452
      if (lig.apply (APPLY_ARG, is_mark))
 
453
        return true;
 
454
    }
 
455
 
 
456
    return false;
 
457
  }
 
458
 
 
459
  public:
 
460
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
461
    TRACE_SANITIZE ();
 
462
    return SANITIZE_THIS (ligature);
 
463
  }
 
464
 
 
465
  private:
 
466
  OffsetArrayOf<Ligature>
 
467
                ligature;               /* Array LigatureSet tables
 
468
                                         * ordered by preference */
 
469
};
 
470
ASSERT_SIZE (LigatureSet, 2);
 
471
 
 
472
struct LigatureSubstFormat1
 
473
{
 
474
  friend struct LigatureSubst;
 
475
 
 
476
  private:
 
477
  inline bool apply (APPLY_ARG_DEF) const
 
478
  {
 
479
    TRACE_APPLY ();
 
480
    hb_codepoint_t glyph_id = IN_CURGLYPH ();
 
481
 
 
482
    bool first_is_mark = !!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
 
483
 
 
484
    unsigned int index = (this+coverage) (glyph_id);
 
485
    if (HB_LIKELY (index == NOT_COVERED))
 
486
      return false;
 
487
 
 
488
    const LigatureSet &lig_set = this+ligatureSet[index];
 
489
    return lig_set.apply (APPLY_ARG, first_is_mark);
 
490
  }
 
491
 
 
492
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
493
    TRACE_SANITIZE ();
 
494
    return SANITIZE_THIS2 (coverage, ligatureSet);
 
495
  }
 
496
 
 
497
  private:
 
498
  USHORT        format;                 /* Format identifier--format = 1 */
 
499
  OffsetTo<Coverage>
 
500
                coverage;               /* Offset to Coverage table--from
 
501
                                         * beginning of Substitution table */
 
502
  OffsetArrayOf<LigatureSet>
 
503
                ligatureSet;            /* Array LigatureSet tables
 
504
                                         * ordered by Coverage Index */
 
505
};
 
506
ASSERT_SIZE (LigatureSubstFormat1, 6);
 
507
 
 
508
struct LigatureSubst
 
509
{
 
510
  friend struct SubstLookupSubTable;
 
511
 
 
512
  private:
 
513
  inline bool apply (APPLY_ARG_DEF) const
 
514
  {
 
515
    TRACE_APPLY ();
 
516
    switch (u.format) {
 
517
    case 1: return u.format1->apply (APPLY_ARG);
 
518
    default:return false;
 
519
    }
 
520
  }
 
521
 
 
522
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
523
    TRACE_SANITIZE ();
 
524
    if (!SANITIZE (u.format)) return false;
 
525
    switch (u.format) {
 
526
    case 1: return u.format1->sanitize (SANITIZE_ARG);
 
527
    default:return true;
 
528
    }
 
529
  }
 
530
 
 
531
  private:
 
532
  union {
 
533
  USHORT                format;         /* Format identifier */
 
534
  LigatureSubstFormat1  format1[];
 
535
  } u;
 
536
};
 
537
ASSERT_SIZE (LigatureSubst, 2);
 
538
 
 
539
 
 
540
 
 
541
static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
 
542
 
 
543
struct ContextSubst : Context
 
544
{
 
545
  friend struct SubstLookupSubTable;
 
546
 
 
547
  private:
 
548
  inline bool apply (APPLY_ARG_DEF) const
 
549
  {
 
550
    TRACE_APPLY ();
 
551
    return Context::apply (APPLY_ARG, substitute_lookup);
 
552
  }
 
553
};
 
554
ASSERT_SIZE (ContextSubst, 2);
 
555
 
 
556
struct ChainContextSubst : ChainContext
 
557
{
 
558
  friend struct SubstLookupSubTable;
 
559
 
 
560
  private:
 
561
  inline bool apply (APPLY_ARG_DEF) const
 
562
  {
 
563
    TRACE_APPLY ();
 
564
    return ChainContext::apply (APPLY_ARG, substitute_lookup);
 
565
  }
 
566
};
 
567
ASSERT_SIZE (ChainContextSubst, 2);
 
568
 
 
569
 
 
570
struct ExtensionSubst : Extension
 
571
{
 
572
  friend struct SubstLookupSubTable;
 
573
 
 
574
  private:
 
575
  inline const struct SubstLookupSubTable& get_subtable (void) const
 
576
  { return CONST_CAST (SubstLookupSubTable, Extension::get_subtable (), 0); }
 
577
 
 
578
  inline bool apply (APPLY_ARG_DEF) const;
 
579
 
 
580
  inline bool sanitize (SANITIZE_ARG_DEF);
 
581
};
 
582
ASSERT_SIZE (ExtensionSubst, 2);
 
583
 
 
584
 
 
585
struct ReverseChainSingleSubstFormat1
 
586
{
 
587
  friend struct ReverseChainSingleSubst;
 
588
 
 
589
  private:
 
590
  inline bool apply (APPLY_ARG_DEF) const
 
591
  {
 
592
    TRACE_APPLY ();
 
593
    if (HB_UNLIKELY (context_length != NO_CONTEXT))
 
594
      return false; /* No chaining to this type */
 
595
 
 
596
    unsigned int index = (this+coverage) (IN_CURGLYPH ());
 
597
    if (HB_LIKELY (index == NOT_COVERED))
 
598
      return false;
 
599
 
 
600
    const OffsetArrayOf<Coverage> &lookahead = CONST_NEXT (OffsetArrayOf<Coverage>, backtrack);
 
601
    const ArrayOf<GlyphID> &substitute = CONST_NEXT (ArrayOf<GlyphID>, lookahead);
 
602
 
 
603
    if (match_backtrack (APPLY_ARG,
 
604
                         backtrack.len, (USHORT *) backtrack.array,
 
605
                         match_coverage, DECONST_CHARP(this)) &&
 
606
        match_lookahead (APPLY_ARG,
 
607
                         lookahead.len, (USHORT *) lookahead.array,
 
608
                         match_coverage, DECONST_CHARP(this),
 
609
                         1))
 
610
    {
 
611
      IN_CURGLYPH () = substitute[index];
 
612
      buffer->in_pos--; /* Reverse! */
 
613
      return true;
 
614
    }
 
615
 
 
616
    return false;
 
617
  }
 
618
 
 
619
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
620
    TRACE_SANITIZE ();
 
621
    if (!SANITIZE_THIS2 (coverage, backtrack))
 
622
      return false;
 
623
    OffsetArrayOf<Coverage> &lookahead = NEXT (OffsetArrayOf<Coverage>, backtrack);
 
624
    if (!SANITIZE_THIS (lookahead))
 
625
      return false;
 
626
    ArrayOf<GlyphID> &substitute = NEXT (ArrayOf<GlyphID>, lookahead);
 
627
    return SANITIZE (substitute);
 
628
  }
 
629
 
 
630
  private:
 
631
  USHORT        format;                 /* Format identifier--format = 1 */
 
632
  OffsetTo<Coverage>
 
633
                coverage;               /* Offset to Coverage table--from
 
634
                                         * beginning of table */
 
635
  OffsetArrayOf<Coverage>
 
636
                backtrack;              /* Array of coverage tables
 
637
                                         * in backtracking sequence, in  glyph
 
638
                                         * sequence order */
 
639
  OffsetArrayOf<Coverage>
 
640
                lookaheadX;             /* Array of coverage tables
 
641
                                         * in lookahead sequence, in glyph
 
642
                                         * sequence order */
 
643
  ArrayOf<GlyphID>
 
644
                substituteX;            /* Array of substitute
 
645
                                         * GlyphIDs--ordered by Coverage Index */
 
646
};
 
647
ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);
 
648
 
 
649
struct ReverseChainSingleSubst
 
650
{
 
651
  friend struct SubstLookupSubTable;
 
652
 
 
653
  private:
 
654
  inline bool apply (APPLY_ARG_DEF) const
 
655
  {
 
656
    TRACE_APPLY ();
 
657
    switch (u.format) {
 
658
    case 1: return u.format1->apply (APPLY_ARG);
 
659
    default:return false;
 
660
    }
 
661
  }
 
662
 
 
663
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
664
    TRACE_SANITIZE ();
 
665
    if (!SANITIZE (u.format)) return false;
 
666
    switch (u.format) {
 
667
    case 1: return u.format1->sanitize (SANITIZE_ARG);
 
668
    default:return true;
 
669
    }
 
670
  }
 
671
 
 
672
  private:
 
673
  union {
 
674
  USHORT                                format;         /* Format identifier */
 
675
  ReverseChainSingleSubstFormat1        format1[];
 
676
  } u;
 
677
};
 
678
ASSERT_SIZE (ReverseChainSingleSubst, 2);
 
679
 
 
680
 
 
681
 
 
682
/*
 
683
 * SubstLookup
 
684
 */
 
685
 
 
686
struct SubstLookupSubTable
 
687
{
 
688
  friend struct SubstLookup;
 
689
 
 
690
  enum {
 
691
    Single              = 1,
 
692
    Multiple            = 2,
 
693
    Alternate           = 3,
 
694
    Ligature            = 4,
 
695
    Context             = 5,
 
696
    ChainContext        = 6,
 
697
    Extension           = 7,
 
698
    ReverseChainSingle  = 8
 
699
  };
 
700
 
 
701
  inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
 
702
  {
 
703
    TRACE_APPLY ();
 
704
    switch (lookup_type) {
 
705
    case Single:                return u.single->apply (APPLY_ARG);
 
706
    case Multiple:              return u.multiple->apply (APPLY_ARG);
 
707
    case Alternate:             return u.alternate->apply (APPLY_ARG);
 
708
    case Ligature:              return u.ligature->apply (APPLY_ARG);
 
709
    case Context:               return u.context->apply (APPLY_ARG);
 
710
    case ChainContext:          return u.chainContext->apply (APPLY_ARG);
 
711
    case Extension:             return u.extension->apply (APPLY_ARG);
 
712
    case ReverseChainSingle:    return u.reverseChainContextSingle->apply (APPLY_ARG);
 
713
    default:return false;
 
714
    }
 
715
  }
 
716
 
 
717
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
718
    TRACE_SANITIZE ();
 
719
    if (!SANITIZE (u.format)) return false;
 
720
    switch (u.format) {
 
721
    case Single:                return u.single->sanitize (SANITIZE_ARG);
 
722
    case Multiple:              return u.multiple->sanitize (SANITIZE_ARG);
 
723
    case Alternate:             return u.alternate->sanitize (SANITIZE_ARG);
 
724
    case Ligature:              return u.ligature->sanitize (SANITIZE_ARG);
 
725
    case Context:               return u.context->sanitize (SANITIZE_ARG);
 
726
    case ChainContext:          return u.chainContext->sanitize (SANITIZE_ARG);
 
727
    case Extension:             return u.extension->sanitize (SANITIZE_ARG);
 
728
    case ReverseChainSingle:    return u.reverseChainContextSingle->sanitize (SANITIZE_ARG);
 
729
    default:return true;
 
730
    }
 
731
  }
 
732
 
 
733
  private:
 
734
  union {
 
735
  USHORT                        format;
 
736
  SingleSubst                   single[];
 
737
  MultipleSubst                 multiple[];
 
738
  AlternateSubst                alternate[];
 
739
  LigatureSubst                 ligature[];
 
740
  ContextSubst                  context[];
 
741
  ChainContextSubst             chainContext[];
 
742
  ExtensionSubst                extension[];
 
743
  ReverseChainSingleSubst       reverseChainContextSingle[];
 
744
  } u;
 
745
};
 
746
ASSERT_SIZE (SubstLookupSubTable, 2);
 
747
 
 
748
 
 
749
struct SubstLookup : Lookup
 
750
{
 
751
  inline const SubstLookupSubTable& get_subtable (unsigned int i) const
 
752
  { return CONST_CAST (SubstLookupSubTable, Lookup::get_subtable (i), 0); }
 
753
 
 
754
  /* Like get_type(), but looks through extension lookups.
 
755
   * Never returns Extension */
 
756
  inline unsigned int get_effective_type (void) const
 
757
  {
 
758
    unsigned int type = get_type ();
 
759
 
 
760
    if (HB_UNLIKELY (type == SubstLookupSubTable::Extension))
 
761
    {
 
762
      unsigned int count = get_subtable_count ();
 
763
      type = get_subtable(0).u.extension->get_type ();
 
764
      /* The spec says all subtables should have the same type.
 
765
       * This is specially important if one has a reverse type! */
 
766
      for (unsigned int i = 1; i < count; i++)
 
767
        if (get_subtable(i).u.extension->get_type () != type)
 
768
          return 0;
 
769
    }
 
770
 
 
771
    return type;
 
772
  }
 
773
 
 
774
  inline bool is_reverse (void) const
 
775
  { return HB_UNLIKELY (get_effective_type () == SubstLookupSubTable::ReverseChainSingle); }
 
776
 
 
777
  inline bool apply_once (hb_ot_layout_context_t *context,
 
778
                          hb_buffer_t    *buffer,
 
779
                          unsigned int    context_length,
 
780
                          unsigned int    nesting_level_left) const
 
781
  {
 
782
    unsigned int lookup_type = get_type ();
 
783
    unsigned int lookup_flag = get_flag ();
 
784
    unsigned int property;
 
785
 
 
786
    if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
 
787
      return false;
 
788
 
 
789
    unsigned int count = get_subtable_count ();
 
790
    for (unsigned int i = 0; i < count; i++)
 
791
      if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
 
792
        return true;
 
793
 
 
794
    return false;
 
795
  }
 
796
 
 
797
  inline bool apply_string (hb_ot_layout_context_t *context,
 
798
                            hb_buffer_t *buffer,
 
799
                            hb_mask_t    mask) const
 
800
  {
 
801
    bool ret = false;
 
802
 
 
803
    if (HB_UNLIKELY (!buffer->in_length))
 
804
      return false;
 
805
 
 
806
    if (HB_LIKELY (!is_reverse ()))
 
807
    {
 
808
        /* in/out forward substitution */
 
809
        _hb_buffer_clear_output (buffer);
 
810
        buffer->in_pos = 0;
 
811
        while (buffer->in_pos < buffer->in_length)
 
812
        {
 
813
          if ((~IN_MASK (buffer->in_pos) & mask) &&
 
814
              apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
 
815
            ret = true;
 
816
          else
 
817
            _hb_buffer_next_glyph (buffer);
 
818
 
 
819
        }
 
820
        if (ret)
 
821
          _hb_buffer_swap (buffer);
 
822
    }
 
823
    else
 
824
    {
 
825
        /* in-place backward substitution */
 
826
        buffer->in_pos = buffer->in_length - 1;
 
827
        do
 
828
        {
 
829
          if ((~IN_MASK (buffer->in_pos) & mask) &&
 
830
              apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
 
831
            ret = true;
 
832
          else
 
833
            buffer->in_pos--;
 
834
 
 
835
        }
 
836
        while ((int) buffer->in_pos >= 0);
 
837
    }
 
838
 
 
839
    return ret;
 
840
  }
 
841
 
 
842
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
843
    TRACE_SANITIZE ();
 
844
    if (!Lookup::sanitize (SANITIZE_ARG)) return false;
 
845
    OffsetArrayOf<SubstLookupSubTable> &list = (OffsetArrayOf<SubstLookupSubTable> &) subTable;
 
846
    return SANITIZE_THIS (list);
 
847
  }
 
848
};
 
849
ASSERT_SIZE (SubstLookup, 6);
 
850
 
 
851
typedef OffsetListOf<SubstLookup> SubstLookupList;
 
852
ASSERT_SIZE (SubstLookupList, 2);
 
853
 
 
854
/*
 
855
 * GSUB
 
856
 */
 
857
 
 
858
struct GSUB : GSUBGPOS
 
859
{
 
860
  static const hb_tag_t Tag     = HB_OT_TAG_GSUB;
 
861
 
 
862
  static inline const GSUB& get_for_data (const char *data)
 
863
  { return (const GSUB&) GSUBGPOS::get_for_data (data); }
 
864
 
 
865
  inline const SubstLookup& get_lookup (unsigned int i) const
 
866
  { return (const SubstLookup&) GSUBGPOS::get_lookup (i); }
 
867
 
 
868
  inline bool substitute_lookup (hb_ot_layout_context_t *context,
 
869
                                 hb_buffer_t  *buffer,
 
870
                                 unsigned int  lookup_index,
 
871
                                 hb_mask_t     mask) const
 
872
  { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
 
873
 
 
874
 
 
875
  inline bool sanitize (SANITIZE_ARG_DEF) {
 
876
    TRACE_SANITIZE ();
 
877
    if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
 
878
    OffsetTo<SubstLookupList> &list = CAST(OffsetTo<SubstLookupList>, lookupList, 0);
 
879
    return SANITIZE_THIS (list);
 
880
  }
 
881
};
 
882
ASSERT_SIZE (GSUB, 10);
 
883
 
 
884
 
 
885
/* Out-of-class implementation for methods recursing */
 
886
 
 
887
inline bool ExtensionSubst::apply (APPLY_ARG_DEF) const
 
888
{
 
889
  TRACE_APPLY ();
 
890
  unsigned int lookup_type = get_type ();
 
891
 
 
892
  if (HB_UNLIKELY (lookup_type == SubstLookupSubTable::Extension))
 
893
    return false;
 
894
 
 
895
  return get_subtable ().apply (APPLY_ARG, lookup_type);
 
896
}
 
897
 
 
898
inline bool ExtensionSubst::sanitize (SANITIZE_ARG_DEF)
 
899
{
 
900
  TRACE_SANITIZE ();
 
901
  return Extension::sanitize (SANITIZE_ARG) &&
 
902
         (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
 
903
          get_type () == SubstLookupSubTable::Extension ||
 
904
          DECONST_CAST (SubstLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
 
905
}
 
906
 
 
907
static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
 
908
{
 
909
  const GSUB &gsub = *(context->face->ot_layout.gsub);
 
910
  const SubstLookup &l = gsub.get_lookup (lookup_index);
 
911
 
 
912
  if (HB_UNLIKELY (nesting_level_left == 0))
 
913
    return false;
 
914
  nesting_level_left--;
 
915
 
 
916
  if (HB_UNLIKELY (context_length < 1))
 
917
    return false;
 
918
 
 
919
  return l.apply_once (context, buffer, context_length, nesting_level_left);
 
920
}
 
921
 
 
922
 
 
923
#endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */