1 : /*
2 : * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 : * Copyright © 2010 Google, Inc.
4 : *
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 : * Red Hat Author(s): Behdad Esfahbod
26 : * Google Author(s): Behdad Esfahbod
27 : */
28 :
29 : #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
30 : #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
31 :
32 : #include "hb-buffer-private.hh"
33 : #include "hb-ot-layout-gdef-table.hh"
34 :
35 :
36 : /* buffer var allocations */
37 : #define lig_id() var2.u8[2] /* unique ligature id */
38 : #define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
39 :
40 0 : static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
41 0 : uint8_t lig_id = buffer->next_serial ();
42 0 : if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
43 0 : return lig_id;
44 : }
45 :
46 :
47 :
48 : #ifndef HB_DEBUG_APPLY
49 : #define HB_DEBUG_APPLY (HB_DEBUG+0)
50 : #endif
51 :
52 : #define TRACE_APPLY() \
53 : hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC);
54 :
55 :
56 :
57 : struct hb_apply_context_t
58 : {
59 : unsigned int debug_depth;
60 : hb_font_t *font;
61 : hb_face_t *face;
62 : hb_buffer_t *buffer;
63 : hb_direction_t direction;
64 : hb_mask_t lookup_mask;
65 : unsigned int context_length;
66 : unsigned int nesting_level_left;
67 : unsigned int lookup_props;
68 : unsigned int property; /* propety of first glyph */
69 :
70 : struct mark_skipping_forward_iterator_t
71 : {
72 0 : inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
73 : unsigned int start_index_,
74 : unsigned int num_items_)
75 : {
76 0 : c = c_;
77 0 : idx = start_index_;
78 0 : num_items = num_items_;
79 0 : end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
80 0 : }
81 0 : inline bool has_no_chance (void) const
82 : {
83 0 : return unlikely (num_items && idx + num_items >= end);
84 : }
85 0 : inline bool next (unsigned int *property_out,
86 : unsigned int lookup_props)
87 : {
88 0 : assert (num_items > 0);
89 0 : do
90 : {
91 0 : if (has_no_chance ())
92 0 : return false;
93 0 : idx++;
94 0 : } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
95 0 : num_items--;
96 0 : return true;
97 : }
98 0 : inline bool next (unsigned int *property_out = NULL)
99 : {
100 0 : return next (property_out, c->lookup_props);
101 : }
102 :
103 : unsigned int idx;
104 : private:
105 : hb_apply_context_t *c;
106 : unsigned int num_items;
107 : unsigned int end;
108 : };
109 :
110 : struct mark_skipping_backward_iterator_t
111 : {
112 0 : inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
113 : unsigned int start_index_,
114 : unsigned int num_items_)
115 : {
116 0 : c = c_;
117 0 : idx = start_index_;
118 0 : num_items = num_items_;
119 0 : }
120 0 : inline bool has_no_chance (void) const
121 : {
122 0 : return unlikely (idx < num_items);
123 : }
124 0 : inline bool prev (unsigned int *property_out,
125 : unsigned int lookup_props)
126 : {
127 0 : assert (num_items > 0);
128 0 : do
129 : {
130 0 : if (has_no_chance ())
131 0 : return false;
132 0 : idx--;
133 0 : } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
134 0 : num_items--;
135 0 : return true;
136 : }
137 0 : inline bool prev (unsigned int *property_out = NULL)
138 : {
139 0 : return prev (property_out, c->lookup_props);
140 : }
141 :
142 : unsigned int idx;
143 : private:
144 : hb_apply_context_t *c;
145 : unsigned int num_items;
146 : };
147 :
148 0 : inline bool should_mark_skip_current_glyph (void) const
149 : {
150 0 : return _hb_ot_layout_skip_mark (face, &buffer->info[buffer->idx], lookup_props, NULL);
151 : }
152 :
153 :
154 :
155 0 : inline void replace_glyph (hb_codepoint_t glyph_index) const
156 : {
157 0 : clear_property ();
158 0 : buffer->replace_glyph (glyph_index);
159 0 : }
160 0 : inline void replace_glyphs_be16 (unsigned int num_in,
161 : unsigned int num_out,
162 : const uint16_t *glyph_data_be) const
163 : {
164 0 : clear_property ();
165 0 : buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
166 0 : }
167 :
168 0 : inline void guess_glyph_class (unsigned int klass)
169 : {
170 : /* XXX if ! has gdef */
171 0 : buffer->info[buffer->idx].props_cache() = klass;
172 0 : }
173 :
174 : private:
175 0 : inline void clear_property (void) const
176 : {
177 : /* XXX if has gdef */
178 0 : buffer->info[buffer->idx].props_cache() = 0;
179 0 : }
180 : };
181 :
182 :
183 :
184 : typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
185 : typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
186 :
187 : struct ContextFuncs
188 : {
189 : match_func_t match;
190 : apply_lookup_func_t apply;
191 : };
192 :
193 :
194 0 : static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
195 : {
196 0 : return glyph_id == value;
197 : }
198 :
199 0 : static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
200 : {
201 0 : const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
202 0 : return class_def.get_class (glyph_id) == value;
203 : }
204 :
205 0 : static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
206 : {
207 0 : const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
208 0 : return (data+coverage) (glyph_id) != NOT_COVERED;
209 : }
210 :
211 :
212 0 : static inline bool match_input (hb_apply_context_t *c,
213 : unsigned int count, /* Including the first glyph (not matched) */
214 : const USHORT input[], /* Array of input values--start with second glyph */
215 : match_func_t match_func,
216 : const void *match_data,
217 : unsigned int *context_length_out)
218 : {
219 0 : hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
220 0 : if (skippy_iter.has_no_chance ())
221 0 : return false;
222 :
223 0 : for (unsigned int i = 1; i < count; i++)
224 : {
225 0 : if (!skippy_iter.next ())
226 0 : return false;
227 :
228 0 : if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data)))
229 0 : return false;
230 : }
231 :
232 0 : *context_length_out = skippy_iter.idx - c->buffer->idx + 1;
233 :
234 0 : return true;
235 : }
236 :
237 0 : static inline bool match_backtrack (hb_apply_context_t *c,
238 : unsigned int count,
239 : const USHORT backtrack[],
240 : match_func_t match_func,
241 : const void *match_data)
242 : {
243 0 : hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count);
244 0 : if (skippy_iter.has_no_chance ())
245 0 : return false;
246 :
247 0 : for (unsigned int i = 0; i < count; i++)
248 : {
249 0 : if (!skippy_iter.prev ())
250 0 : return false;
251 :
252 0 : if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data)))
253 0 : return false;
254 : }
255 :
256 0 : return true;
257 : }
258 :
259 0 : static inline bool match_lookahead (hb_apply_context_t *c,
260 : unsigned int count,
261 : const USHORT lookahead[],
262 : match_func_t match_func,
263 : const void *match_data,
264 : unsigned int offset)
265 : {
266 0 : hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count);
267 0 : if (skippy_iter.has_no_chance ())
268 0 : return false;
269 :
270 0 : for (unsigned int i = 0; i < count; i++)
271 : {
272 0 : if (!skippy_iter.next ())
273 0 : return false;
274 :
275 0 : if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data)))
276 0 : return false;
277 : }
278 :
279 0 : return true;
280 : }
281 :
282 :
283 :
284 : struct LookupRecord
285 : {
286 : inline bool sanitize (hb_sanitize_context_t *c) {
287 : TRACE_SANITIZE ();
288 : return c->check_struct (this);
289 : }
290 :
291 : USHORT sequenceIndex; /* Index into current glyph
292 : * sequence--first glyph = 0 */
293 : USHORT lookupListIndex; /* Lookup to apply to that
294 : * position--zero--based */
295 : public:
296 : DEFINE_SIZE_STATIC (4);
297 : };
298 :
299 :
300 :
301 0 : static inline bool apply_lookup (hb_apply_context_t *c,
302 : unsigned int count, /* Including the first glyph */
303 : unsigned int lookupCount,
304 : const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
305 : apply_lookup_func_t apply_func)
306 : {
307 0 : unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
308 0 : if (unlikely (count == 0 || c->buffer->idx + count > end))
309 0 : return false;
310 :
311 : /* TODO We don't support lookupRecord arrays that are not increasing:
312 : * Should be easy for in_place ones at least. */
313 :
314 : /* Note: If sublookup is reverse, it will underflow after the first loop
315 : * and we jump out of it. Not entirely disastrous. So we don't check
316 : * for reverse lookup here.
317 : */
318 0 : for (unsigned int i = 0; i < count; /* NOP */)
319 : {
320 0 : if (unlikely (c->buffer->idx == end))
321 0 : return true;
322 0 : while (c->should_mark_skip_current_glyph ())
323 : {
324 : /* No lookup applied for this index */
325 0 : c->buffer->next_glyph ();
326 0 : if (unlikely (c->buffer->idx == end))
327 0 : return true;
328 : }
329 :
330 0 : if (lookupCount && i == lookupRecord->sequenceIndex)
331 : {
332 0 : unsigned int old_pos = c->buffer->idx;
333 :
334 : /* Apply a lookup */
335 0 : bool done = apply_func (c, lookupRecord->lookupListIndex);
336 :
337 0 : lookupRecord++;
338 0 : lookupCount--;
339 : /* Err, this is wrong if the lookup jumped over some glyphs */
340 0 : i += c->buffer->idx - old_pos;
341 0 : if (unlikely (c->buffer->idx == end))
342 0 : return true;
343 :
344 0 : if (!done)
345 0 : goto not_applied;
346 : }
347 : else
348 : {
349 : not_applied:
350 : /* No lookup applied for this index */
351 0 : c->buffer->next_glyph ();
352 0 : i++;
353 : }
354 : }
355 :
356 0 : return true;
357 : }
358 :
359 :
360 :
361 : /* Contextual lookups */
362 :
363 : struct ContextLookupContext
364 : {
365 : ContextFuncs funcs;
366 : const void *match_data;
367 : };
368 :
369 0 : static inline bool context_lookup (hb_apply_context_t *c,
370 : unsigned int inputCount, /* Including the first glyph (not matched) */
371 : const USHORT input[], /* Array of input values--start with second glyph */
372 : unsigned int lookupCount,
373 : const LookupRecord lookupRecord[],
374 : ContextLookupContext &lookup_context)
375 : {
376 0 : hb_apply_context_t new_context = *c;
377 : return match_input (c,
378 : inputCount, input,
379 : lookup_context.funcs.match, lookup_context.match_data,
380 0 : &new_context.context_length)
381 : && apply_lookup (&new_context,
382 : inputCount,
383 : lookupCount, lookupRecord,
384 0 : lookup_context.funcs.apply);
385 : }
386 :
387 : struct Rule
388 : {
389 : friend struct RuleSet;
390 :
391 : private:
392 0 : inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
393 : {
394 0 : TRACE_APPLY ();
395 0 : const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
396 : return context_lookup (c,
397 : inputCount, input,
398 : lookupCount, lookupRecord,
399 0 : lookup_context);
400 : }
401 :
402 : public:
403 0 : inline bool sanitize (hb_sanitize_context_t *c) {
404 0 : TRACE_SANITIZE ();
405 0 : return inputCount.sanitize (c)
406 0 : && lookupCount.sanitize (c)
407 : && c->check_range (input,
408 0 : input[0].static_size * inputCount
409 0 : + lookupRecordX[0].static_size * lookupCount);
410 : }
411 :
412 : private:
413 : USHORT inputCount; /* Total number of glyphs in input
414 : * glyph sequence--includes the first
415 : * glyph */
416 : USHORT lookupCount; /* Number of LookupRecords */
417 : USHORT input[VAR]; /* Array of match inputs--start with
418 : * second glyph */
419 : LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in
420 : * design order */
421 : public:
422 : DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
423 : };
424 :
425 : struct RuleSet
426 : {
427 0 : inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
428 : {
429 0 : TRACE_APPLY ();
430 0 : unsigned int num_rules = rule.len;
431 0 : for (unsigned int i = 0; i < num_rules; i++)
432 : {
433 0 : if ((this+rule[i]).apply (c, lookup_context))
434 0 : return true;
435 : }
436 :
437 0 : return false;
438 : }
439 :
440 0 : inline bool sanitize (hb_sanitize_context_t *c) {
441 0 : TRACE_SANITIZE ();
442 0 : return rule.sanitize (c, this);
443 : }
444 :
445 : private:
446 : OffsetArrayOf<Rule>
447 : rule; /* Array of Rule tables
448 : * ordered by preference */
449 : public:
450 : DEFINE_SIZE_ARRAY (2, rule);
451 : };
452 :
453 :
454 : struct ContextFormat1
455 : {
456 : friend struct Context;
457 :
458 : private:
459 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
460 : {
461 0 : TRACE_APPLY ();
462 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
463 0 : if (likely (index == NOT_COVERED))
464 0 : return false;
465 :
466 0 : const RuleSet &rule_set = this+ruleSet[index];
467 : struct ContextLookupContext lookup_context = {
468 : {match_glyph, apply_func},
469 : NULL
470 0 : };
471 0 : return rule_set.apply (c, lookup_context);
472 : }
473 :
474 0 : inline bool sanitize (hb_sanitize_context_t *c) {
475 0 : TRACE_SANITIZE ();
476 0 : return coverage.sanitize (c, this)
477 0 : && ruleSet.sanitize (c, this);
478 : }
479 :
480 : private:
481 : USHORT format; /* Format identifier--format = 1 */
482 : OffsetTo<Coverage>
483 : coverage; /* Offset to Coverage table--from
484 : * beginning of table */
485 : OffsetArrayOf<RuleSet>
486 : ruleSet; /* Array of RuleSet tables
487 : * ordered by Coverage Index */
488 : public:
489 : DEFINE_SIZE_ARRAY (6, ruleSet);
490 : };
491 :
492 :
493 : struct ContextFormat2
494 : {
495 : friend struct Context;
496 :
497 : private:
498 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
499 : {
500 0 : TRACE_APPLY ();
501 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
502 0 : if (likely (index == NOT_COVERED))
503 0 : return false;
504 :
505 0 : const ClassDef &class_def = this+classDef;
506 0 : index = class_def (c->buffer->info[c->buffer->idx].codepoint);
507 0 : const RuleSet &rule_set = this+ruleSet[index];
508 : struct ContextLookupContext lookup_context = {
509 : {match_class, apply_func},
510 : &class_def
511 0 : };
512 0 : return rule_set.apply (c, lookup_context);
513 : }
514 :
515 0 : inline bool sanitize (hb_sanitize_context_t *c) {
516 0 : TRACE_SANITIZE ();
517 0 : return coverage.sanitize (c, this)
518 0 : && classDef.sanitize (c, this)
519 0 : && ruleSet.sanitize (c, this);
520 : }
521 :
522 : private:
523 : USHORT format; /* Format identifier--format = 2 */
524 : OffsetTo<Coverage>
525 : coverage; /* Offset to Coverage table--from
526 : * beginning of table */
527 : OffsetTo<ClassDef>
528 : classDef; /* Offset to glyph ClassDef table--from
529 : * beginning of table */
530 : OffsetArrayOf<RuleSet>
531 : ruleSet; /* Array of RuleSet tables
532 : * ordered by class */
533 : public:
534 : DEFINE_SIZE_ARRAY (8, ruleSet);
535 : };
536 :
537 :
538 : struct ContextFormat3
539 : {
540 : friend struct Context;
541 :
542 : private:
543 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
544 : {
545 0 : TRACE_APPLY ();
546 0 : unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint);
547 0 : if (likely (index == NOT_COVERED))
548 0 : return false;
549 :
550 0 : const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
551 : struct ContextLookupContext lookup_context = {
552 : {match_coverage, apply_func},
553 : this
554 0 : };
555 : return context_lookup (c,
556 0 : glyphCount, (const USHORT *) (coverage + 1),
557 : lookupCount, lookupRecord,
558 0 : lookup_context);
559 : }
560 :
561 0 : inline bool sanitize (hb_sanitize_context_t *c) {
562 0 : TRACE_SANITIZE ();
563 0 : if (!c->check_struct (this)) return false;
564 0 : unsigned int count = glyphCount;
565 0 : if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
566 0 : for (unsigned int i = 0; i < count; i++)
567 0 : if (!coverage[i].sanitize (c, this)) return false;
568 0 : LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
569 0 : return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
570 : }
571 :
572 : private:
573 : USHORT format; /* Format identifier--format = 3 */
574 : USHORT glyphCount; /* Number of glyphs in the input glyph
575 : * sequence */
576 : USHORT lookupCount; /* Number of LookupRecords */
577 : OffsetTo<Coverage>
578 : coverage[VAR]; /* Array of offsets to Coverage
579 : * table in glyph sequence order */
580 : LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in
581 : * design order */
582 : public:
583 : DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
584 : };
585 :
586 : struct Context
587 : {
588 : protected:
589 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
590 : {
591 0 : TRACE_APPLY ();
592 0 : switch (u.format) {
593 0 : case 1: return u.format1.apply (c, apply_func);
594 0 : case 2: return u.format2.apply (c, apply_func);
595 0 : case 3: return u.format3.apply (c, apply_func);
596 0 : default:return false;
597 : }
598 : }
599 :
600 0 : inline bool sanitize (hb_sanitize_context_t *c) {
601 0 : TRACE_SANITIZE ();
602 0 : if (!u.format.sanitize (c)) return false;
603 0 : switch (u.format) {
604 0 : case 1: return u.format1.sanitize (c);
605 0 : case 2: return u.format2.sanitize (c);
606 0 : case 3: return u.format3.sanitize (c);
607 0 : default:return true;
608 : }
609 : }
610 :
611 : private:
612 : union {
613 : USHORT format; /* Format identifier */
614 : ContextFormat1 format1;
615 : ContextFormat2 format2;
616 : ContextFormat3 format3;
617 : } u;
618 : };
619 :
620 :
621 : /* Chaining Contextual lookups */
622 :
623 : struct ChainContextLookupContext
624 : {
625 : ContextFuncs funcs;
626 : const void *match_data[3];
627 : };
628 :
629 0 : static inline bool chain_context_lookup (hb_apply_context_t *c,
630 : unsigned int backtrackCount,
631 : const USHORT backtrack[],
632 : unsigned int inputCount, /* Including the first glyph (not matched) */
633 : const USHORT input[], /* Array of input values--start with second glyph */
634 : unsigned int lookaheadCount,
635 : const USHORT lookahead[],
636 : unsigned int lookupCount,
637 : const LookupRecord lookupRecord[],
638 : ChainContextLookupContext &lookup_context)
639 : {
640 : /* First guess */
641 0 : if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
642 : c->buffer->idx + inputCount + lookaheadCount > c->buffer->len ||
643 : inputCount + lookaheadCount > c->context_length))
644 0 : return false;
645 :
646 0 : hb_apply_context_t new_context = *c;
647 : return match_backtrack (c,
648 : backtrackCount, backtrack,
649 0 : lookup_context.funcs.match, lookup_context.match_data[0])
650 : && match_input (c,
651 : inputCount, input,
652 : lookup_context.funcs.match, lookup_context.match_data[1],
653 0 : &new_context.context_length)
654 : && match_lookahead (c,
655 : lookaheadCount, lookahead,
656 : lookup_context.funcs.match, lookup_context.match_data[2],
657 0 : new_context.context_length)
658 : && apply_lookup (&new_context,
659 : inputCount,
660 : lookupCount, lookupRecord,
661 0 : lookup_context.funcs.apply);
662 : }
663 :
664 : struct ChainRule
665 : {
666 : friend struct ChainRuleSet;
667 :
668 : private:
669 0 : inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
670 : {
671 0 : TRACE_APPLY ();
672 0 : const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
673 0 : const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
674 0 : const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
675 : return chain_context_lookup (c,
676 : backtrack.len, backtrack.array,
677 : input.len, input.array,
678 : lookahead.len, lookahead.array,
679 : lookup.len, lookup.array,
680 0 : lookup_context);
681 : }
682 :
683 : public:
684 0 : inline bool sanitize (hb_sanitize_context_t *c) {
685 0 : TRACE_SANITIZE ();
686 0 : if (!backtrack.sanitize (c)) return false;
687 0 : HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
688 0 : if (!input.sanitize (c)) return false;
689 0 : ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
690 0 : if (!lookahead.sanitize (c)) return false;
691 0 : ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
692 0 : return lookup.sanitize (c);
693 : }
694 :
695 : private:
696 : ArrayOf<USHORT>
697 : backtrack; /* Array of backtracking values
698 : * (to be matched before the input
699 : * sequence) */
700 : HeadlessArrayOf<USHORT>
701 : inputX; /* Array of input values (start with
702 : * second glyph) */
703 : ArrayOf<USHORT>
704 : lookaheadX; /* Array of lookahead values's (to be
705 : * matched after the input sequence) */
706 : ArrayOf<LookupRecord>
707 : lookupX; /* Array of LookupRecords--in
708 : * design order) */
709 : public:
710 : DEFINE_SIZE_MIN (8);
711 : };
712 :
713 : struct ChainRuleSet
714 : {
715 0 : inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
716 : {
717 0 : TRACE_APPLY ();
718 0 : unsigned int num_rules = rule.len;
719 0 : for (unsigned int i = 0; i < num_rules; i++)
720 : {
721 0 : if ((this+rule[i]).apply (c, lookup_context))
722 0 : return true;
723 : }
724 :
725 0 : return false;
726 : }
727 :
728 0 : inline bool sanitize (hb_sanitize_context_t *c) {
729 0 : TRACE_SANITIZE ();
730 0 : return rule.sanitize (c, this);
731 : }
732 :
733 : private:
734 : OffsetArrayOf<ChainRule>
735 : rule; /* Array of ChainRule tables
736 : * ordered by preference */
737 : public:
738 : DEFINE_SIZE_ARRAY (2, rule);
739 : };
740 :
741 : struct ChainContextFormat1
742 : {
743 : friend struct ChainContext;
744 :
745 : private:
746 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
747 : {
748 0 : TRACE_APPLY ();
749 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
750 0 : if (likely (index == NOT_COVERED))
751 0 : return false;
752 :
753 0 : const ChainRuleSet &rule_set = this+ruleSet[index];
754 : struct ChainContextLookupContext lookup_context = {
755 : {match_glyph, apply_func},
756 : {NULL, NULL, NULL}
757 0 : };
758 0 : return rule_set.apply (c, lookup_context);
759 : }
760 :
761 0 : inline bool sanitize (hb_sanitize_context_t *c) {
762 0 : TRACE_SANITIZE ();
763 0 : return coverage.sanitize (c, this)
764 0 : && ruleSet.sanitize (c, this);
765 : }
766 :
767 : private:
768 : USHORT format; /* Format identifier--format = 1 */
769 : OffsetTo<Coverage>
770 : coverage; /* Offset to Coverage table--from
771 : * beginning of table */
772 : OffsetArrayOf<ChainRuleSet>
773 : ruleSet; /* Array of ChainRuleSet tables
774 : * ordered by Coverage Index */
775 : public:
776 : DEFINE_SIZE_ARRAY (6, ruleSet);
777 : };
778 :
779 : struct ChainContextFormat2
780 : {
781 : friend struct ChainContext;
782 :
783 : private:
784 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
785 : {
786 0 : TRACE_APPLY ();
787 0 : unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
788 0 : if (likely (index == NOT_COVERED))
789 0 : return false;
790 :
791 0 : const ClassDef &backtrack_class_def = this+backtrackClassDef;
792 0 : const ClassDef &input_class_def = this+inputClassDef;
793 0 : const ClassDef &lookahead_class_def = this+lookaheadClassDef;
794 :
795 0 : index = input_class_def (c->buffer->info[c->buffer->idx].codepoint);
796 0 : const ChainRuleSet &rule_set = this+ruleSet[index];
797 : struct ChainContextLookupContext lookup_context = {
798 : {match_class, apply_func},
799 : {&backtrack_class_def,
800 : &input_class_def,
801 : &lookahead_class_def}
802 0 : };
803 0 : return rule_set.apply (c, lookup_context);
804 : }
805 :
806 0 : inline bool sanitize (hb_sanitize_context_t *c) {
807 0 : TRACE_SANITIZE ();
808 0 : return coverage.sanitize (c, this)
809 0 : && backtrackClassDef.sanitize (c, this)
810 0 : && inputClassDef.sanitize (c, this)
811 0 : && lookaheadClassDef.sanitize (c, this)
812 0 : && ruleSet.sanitize (c, this);
813 : }
814 :
815 : private:
816 : USHORT format; /* Format identifier--format = 2 */
817 : OffsetTo<Coverage>
818 : coverage; /* Offset to Coverage table--from
819 : * beginning of table */
820 : OffsetTo<ClassDef>
821 : backtrackClassDef; /* Offset to glyph ClassDef table
822 : * containing backtrack sequence
823 : * data--from beginning of table */
824 : OffsetTo<ClassDef>
825 : inputClassDef; /* Offset to glyph ClassDef
826 : * table containing input sequence
827 : * data--from beginning of table */
828 : OffsetTo<ClassDef>
829 : lookaheadClassDef; /* Offset to glyph ClassDef table
830 : * containing lookahead sequence
831 : * data--from beginning of table */
832 : OffsetArrayOf<ChainRuleSet>
833 : ruleSet; /* Array of ChainRuleSet tables
834 : * ordered by class */
835 : public:
836 : DEFINE_SIZE_ARRAY (12, ruleSet);
837 : };
838 :
839 : struct ChainContextFormat3
840 : {
841 : friend struct ChainContext;
842 :
843 : private:
844 :
845 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
846 : {
847 0 : TRACE_APPLY ();
848 0 : const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
849 :
850 0 : unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint);
851 0 : if (likely (index == NOT_COVERED))
852 0 : return false;
853 :
854 0 : const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
855 0 : const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
856 : struct ChainContextLookupContext lookup_context = {
857 : {match_coverage, apply_func},
858 : {this, this, this}
859 0 : };
860 : return chain_context_lookup (c,
861 : backtrack.len, (const USHORT *) backtrack.array,
862 : input.len, (const USHORT *) input.array + 1,
863 : lookahead.len, (const USHORT *) lookahead.array,
864 : lookup.len, lookup.array,
865 0 : lookup_context);
866 : }
867 :
868 0 : inline bool sanitize (hb_sanitize_context_t *c) {
869 0 : TRACE_SANITIZE ();
870 0 : if (!backtrack.sanitize (c, this)) return false;
871 0 : OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
872 0 : if (!input.sanitize (c, this)) return false;
873 0 : OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
874 0 : if (!lookahead.sanitize (c, this)) return false;
875 0 : ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
876 0 : return lookup.sanitize (c);
877 : }
878 :
879 : private:
880 : USHORT format; /* Format identifier--format = 3 */
881 : OffsetArrayOf<Coverage>
882 : backtrack; /* Array of coverage tables
883 : * in backtracking sequence, in glyph
884 : * sequence order */
885 : OffsetArrayOf<Coverage>
886 : inputX ; /* Array of coverage
887 : * tables in input sequence, in glyph
888 : * sequence order */
889 : OffsetArrayOf<Coverage>
890 : lookaheadX; /* Array of coverage tables
891 : * in lookahead sequence, in glyph
892 : * sequence order */
893 : ArrayOf<LookupRecord>
894 : lookupX; /* Array of LookupRecords--in
895 : * design order) */
896 : public:
897 : DEFINE_SIZE_MIN (10);
898 : };
899 :
900 : struct ChainContext
901 : {
902 : protected:
903 0 : inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
904 : {
905 0 : TRACE_APPLY ();
906 0 : switch (u.format) {
907 0 : case 1: return u.format1.apply (c, apply_func);
908 0 : case 2: return u.format2.apply (c, apply_func);
909 0 : case 3: return u.format3.apply (c, apply_func);
910 0 : default:return false;
911 : }
912 : }
913 :
914 0 : inline bool sanitize (hb_sanitize_context_t *c) {
915 0 : TRACE_SANITIZE ();
916 0 : if (!u.format.sanitize (c)) return false;
917 0 : switch (u.format) {
918 0 : case 1: return u.format1.sanitize (c);
919 0 : case 2: return u.format2.sanitize (c);
920 0 : case 3: return u.format3.sanitize (c);
921 0 : default:return true;
922 : }
923 : }
924 :
925 : private:
926 : union {
927 : USHORT format; /* Format identifier */
928 : ChainContextFormat1 format1;
929 : ChainContextFormat2 format2;
930 : ChainContextFormat3 format3;
931 : } u;
932 : };
933 :
934 :
935 : struct ExtensionFormat1
936 : {
937 : friend struct Extension;
938 :
939 : protected:
940 0 : inline unsigned int get_type (void) const { return extensionLookupType; }
941 0 : inline unsigned int get_offset (void) const { return extensionOffset; }
942 :
943 0 : inline bool sanitize (hb_sanitize_context_t *c) {
944 0 : TRACE_SANITIZE ();
945 0 : return c->check_struct (this);
946 : }
947 :
948 : private:
949 : USHORT format; /* Format identifier. Set to 1. */
950 : USHORT extensionLookupType; /* Lookup type of subtable referenced
951 : * by ExtensionOffset (i.e. the
952 : * extension subtable). */
953 : ULONG extensionOffset; /* Offset to the extension subtable,
954 : * of lookup type subtable. */
955 : public:
956 : DEFINE_SIZE_STATIC (8);
957 : };
958 :
959 : struct Extension
960 : {
961 0 : inline unsigned int get_type (void) const
962 : {
963 0 : switch (u.format) {
964 0 : case 1: return u.format1.get_type ();
965 0 : default:return 0;
966 : }
967 : }
968 0 : inline unsigned int get_offset (void) const
969 : {
970 0 : switch (u.format) {
971 0 : case 1: return u.format1.get_offset ();
972 0 : default:return 0;
973 : }
974 : }
975 :
976 0 : inline bool sanitize (hb_sanitize_context_t *c) {
977 0 : TRACE_SANITIZE ();
978 0 : if (!u.format.sanitize (c)) return false;
979 0 : switch (u.format) {
980 0 : case 1: return u.format1.sanitize (c);
981 0 : default:return true;
982 : }
983 : }
984 :
985 : private:
986 : union {
987 : USHORT format; /* Format identifier */
988 : ExtensionFormat1 format1;
989 : } u;
990 : };
991 :
992 :
993 : /*
994 : * GSUB/GPOS Common
995 : */
996 :
997 : struct GSUBGPOS
998 : {
999 : static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB;
1000 : static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS;
1001 :
1002 : inline unsigned int get_script_count (void) const
1003 : { return (this+scriptList).len; }
1004 : inline const Tag& get_script_tag (unsigned int i) const
1005 : { return (this+scriptList).get_tag (i); }
1006 0 : inline unsigned int get_script_tags (unsigned int start_offset,
1007 : unsigned int *script_count /* IN/OUT */,
1008 : hb_tag_t *script_tags /* OUT */) const
1009 0 : { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
1010 0 : inline const Script& get_script (unsigned int i) const
1011 0 : { return (this+scriptList)[i]; }
1012 0 : inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
1013 0 : { return (this+scriptList).find_index (tag, index); }
1014 :
1015 : inline unsigned int get_feature_count (void) const
1016 : { return (this+featureList).len; }
1017 0 : inline const Tag& get_feature_tag (unsigned int i) const
1018 0 : { return (this+featureList).get_tag (i); }
1019 0 : inline unsigned int get_feature_tags (unsigned int start_offset,
1020 : unsigned int *feature_count /* IN/OUT */,
1021 : hb_tag_t *feature_tags /* OUT */) const
1022 0 : { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
1023 0 : inline const Feature& get_feature (unsigned int i) const
1024 0 : { return (this+featureList)[i]; }
1025 : inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
1026 : { return (this+featureList).find_index (tag, index); }
1027 :
1028 : inline unsigned int get_lookup_count (void) const
1029 : { return (this+lookupList).len; }
1030 0 : inline const Lookup& get_lookup (unsigned int i) const
1031 0 : { return (this+lookupList)[i]; }
1032 :
1033 0 : inline bool sanitize (hb_sanitize_context_t *c) {
1034 0 : TRACE_SANITIZE ();
1035 0 : return version.sanitize (c) && likely (version.major == 1)
1036 0 : && scriptList.sanitize (c, this)
1037 0 : && featureList.sanitize (c, this)
1038 0 : && lookupList.sanitize (c, this);
1039 : }
1040 :
1041 : protected:
1042 : FixedVersion version; /* Version of the GSUB/GPOS table--initially set
1043 : * to 0x00010000 */
1044 : OffsetTo<ScriptList>
1045 : scriptList; /* ScriptList table */
1046 : OffsetTo<FeatureList>
1047 : featureList; /* FeatureList table */
1048 : OffsetTo<LookupList>
1049 : lookupList; /* LookupList table */
1050 : public:
1051 : DEFINE_SIZE_STATIC (10);
1052 : };
1053 :
1054 :
1055 :
1056 : #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
|