OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc. | |
3 * Copyright (C) 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_GSUB_PRIVATE_HH | |
30 #define HB_OT_LAYOUT_GSUB_PRIVATE_HH | |
31 | |
32 #include "hb-ot-layout-gsubgpos-private.hh" | |
33 | |
34 HB_BEGIN_DECLS | |
35 | |
36 | |
37 struct SingleSubstFormat1 | |
38 { | |
39 friend struct SingleSubst; | |
40 | |
41 private: | |
42 | |
43 inline bool apply (hb_apply_context_t *c) const | |
44 { | |
45 TRACE_APPLY (); | |
46 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint; | |
47 unsigned int index = (this+coverage) (glyph_id); | |
48 if (likely (index == NOT_COVERED)) | |
49 return false; | |
50 | |
51 glyph_id += deltaGlyphID; | |
52 c->replace_glyph (glyph_id); | |
53 | |
54 return true; | |
55 } | |
56 | |
57 inline bool sanitize (hb_sanitize_context_t *c) { | |
58 TRACE_SANITIZE (); | |
59 return coverage.sanitize (c, this) | |
60 && deltaGlyphID.sanitize (c); | |
61 } | |
62 | |
63 private: | |
64 USHORT format; /* Format identifier--format = 1 */ | |
65 OffsetTo<Coverage> | |
66 coverage; /* Offset to Coverage table--from | |
67 * beginning of Substitution table */ | |
68 SHORT deltaGlyphID; /* Add to original GlyphID to get | |
69 * substitute GlyphID */ | |
70 public: | |
71 DEFINE_SIZE_STATIC (6); | |
72 }; | |
73 | |
74 struct SingleSubstFormat2 | |
75 { | |
76 friend struct SingleSubst; | |
77 | |
78 private: | |
79 | |
80 inline bool apply (hb_apply_context_t *c) const | |
81 { | |
82 TRACE_APPLY (); | |
83 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint; | |
84 unsigned int index = (this+coverage) (glyph_id); | |
85 if (likely (index == NOT_COVERED)) | |
86 return false; | |
87 | |
88 if (unlikely (index >= substitute.len)) | |
89 return false; | |
90 | |
91 glyph_id = substitute[index]; | |
92 c->replace_glyph (glyph_id); | |
93 | |
94 return true; | |
95 } | |
96 | |
97 inline bool sanitize (hb_sanitize_context_t *c) { | |
98 TRACE_SANITIZE (); | |
99 return coverage.sanitize (c, this) | |
100 && substitute.sanitize (c); | |
101 } | |
102 | |
103 private: | |
104 USHORT format; /* Format identifier--format = 2 */ | |
105 OffsetTo<Coverage> | |
106 coverage; /* Offset to Coverage table--from | |
107 * beginning of Substitution table */ | |
108 ArrayOf<GlyphID> | |
109 substitute; /* Array of substitute | |
110 * GlyphIDs--ordered by Coverage Index *
/ | |
111 public: | |
112 DEFINE_SIZE_ARRAY (6, substitute); | |
113 }; | |
114 | |
115 struct SingleSubst | |
116 { | |
117 friend struct SubstLookupSubTable; | |
118 | |
119 private: | |
120 | |
121 inline bool apply (hb_apply_context_t *c) const | |
122 { | |
123 TRACE_APPLY (); | |
124 switch (u.format) { | |
125 case 1: return u.format1.apply (c); | |
126 case 2: return u.format2.apply (c); | |
127 default:return false; | |
128 } | |
129 } | |
130 | |
131 inline bool sanitize (hb_sanitize_context_t *c) { | |
132 TRACE_SANITIZE (); | |
133 if (!u.format.sanitize (c)) return false; | |
134 switch (u.format) { | |
135 case 1: return u.format1.sanitize (c); | |
136 case 2: return u.format2.sanitize (c); | |
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 | |
149 | |
150 struct Sequence | |
151 { | |
152 friend struct MultipleSubstFormat1; | |
153 | |
154 private: | |
155 inline bool apply (hb_apply_context_t *c) const | |
156 { | |
157 TRACE_APPLY (); | |
158 if (unlikely (!substitute.len)) | |
159 return false; | |
160 | |
161 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) | |
162 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH); | |
163 c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.arr
ay); | |
164 | |
165 return true; | |
166 } | |
167 | |
168 public: | |
169 inline bool sanitize (hb_sanitize_context_t *c) { | |
170 TRACE_SANITIZE (); | |
171 return substitute.sanitize (c); | |
172 } | |
173 | |
174 private: | |
175 ArrayOf<GlyphID> | |
176 substitute; /* String of GlyphIDs to substitute */ | |
177 public: | |
178 DEFINE_SIZE_ARRAY (2, substitute); | |
179 }; | |
180 | |
181 struct MultipleSubstFormat1 | |
182 { | |
183 friend struct MultipleSubst; | |
184 | |
185 private: | |
186 | |
187 inline bool apply (hb_apply_context_t *c) const | |
188 { | |
189 TRACE_APPLY (); | |
190 | |
191 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoin
t); | |
192 if (likely (index == NOT_COVERED)) | |
193 return false; | |
194 | |
195 return (this+sequence[index]).apply (c); | |
196 } | |
197 | |
198 inline bool sanitize (hb_sanitize_context_t *c) { | |
199 TRACE_SANITIZE (); | |
200 return coverage.sanitize (c, this) | |
201 && sequence.sanitize (c, this); | |
202 } | |
203 | |
204 private: | |
205 USHORT format; /* Format identifier--format = 1 */ | |
206 OffsetTo<Coverage> | |
207 coverage; /* Offset to Coverage table--from | |
208 * beginning of Substitution table */ | |
209 OffsetArrayOf<Sequence> | |
210 sequence; /* Array of Sequence tables | |
211 * ordered by Coverage Index */ | |
212 public: | |
213 DEFINE_SIZE_ARRAY (6, sequence); | |
214 }; | |
215 | |
216 struct MultipleSubst | |
217 { | |
218 friend struct SubstLookupSubTable; | |
219 | |
220 private: | |
221 | |
222 inline bool apply (hb_apply_context_t *c) const | |
223 { | |
224 TRACE_APPLY (); | |
225 switch (u.format) { | |
226 case 1: return u.format1.apply (c); | |
227 default:return false; | |
228 } | |
229 } | |
230 | |
231 inline bool sanitize (hb_sanitize_context_t *c) { | |
232 TRACE_SANITIZE (); | |
233 if (!u.format.sanitize (c)) return false; | |
234 switch (u.format) { | |
235 case 1: return u.format1.sanitize (c); | |
236 default:return true; | |
237 } | |
238 } | |
239 | |
240 private: | |
241 union { | |
242 USHORT format; /* Format identifier */ | |
243 MultipleSubstFormat1 format1; | |
244 } u; | |
245 }; | |
246 | |
247 | |
248 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in | |
249 * arbitrary order */ | |
250 | |
251 struct AlternateSubstFormat1 | |
252 { | |
253 friend struct AlternateSubst; | |
254 | |
255 private: | |
256 | |
257 inline bool apply (hb_apply_context_t *c) const | |
258 { | |
259 TRACE_APPLY (); | |
260 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint; | |
261 hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].mask; | |
262 hb_mask_t lookup_mask = c->lookup_mask; | |
263 | |
264 unsigned int index = (this+coverage) (glyph_id); | |
265 if (likely (index == NOT_COVERED)) | |
266 return false; | |
267 | |
268 const AlternateSet &alt_set = this+alternateSet[index]; | |
269 | |
270 if (unlikely (!alt_set.len)) | |
271 return false; | |
272 | |
273 /* Note: This breaks badly if two features enabled this lookup together. */ | |
274 unsigned int shift = _hb_ctz (lookup_mask); | |
275 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); | |
276 | |
277 if (unlikely (alt_index > alt_set.len || alt_index == 0)) | |
278 return false; | |
279 | |
280 glyph_id = alt_set[alt_index - 1]; | |
281 | |
282 c->replace_glyph (glyph_id); | |
283 | |
284 return true; | |
285 } | |
286 | |
287 inline bool sanitize (hb_sanitize_context_t *c) { | |
288 TRACE_SANITIZE (); | |
289 return coverage.sanitize (c, this) | |
290 && alternateSet.sanitize (c, this); | |
291 } | |
292 | |
293 private: | |
294 USHORT format; /* Format identifier--format = 1 */ | |
295 OffsetTo<Coverage> | |
296 coverage; /* Offset to Coverage table--from | |
297 * beginning of Substitution table */ | |
298 OffsetArrayOf<AlternateSet> | |
299 alternateSet; /* Array of AlternateSet tables | |
300 * ordered by Coverage Index */ | |
301 public: | |
302 DEFINE_SIZE_ARRAY (6, alternateSet); | |
303 }; | |
304 | |
305 struct AlternateSubst | |
306 { | |
307 friend struct SubstLookupSubTable; | |
308 | |
309 private: | |
310 | |
311 inline bool apply (hb_apply_context_t *c) const | |
312 { | |
313 TRACE_APPLY (); | |
314 switch (u.format) { | |
315 case 1: return u.format1.apply (c); | |
316 default:return false; | |
317 } | |
318 } | |
319 | |
320 inline bool sanitize (hb_sanitize_context_t *c) { | |
321 TRACE_SANITIZE (); | |
322 if (!u.format.sanitize (c)) return false; | |
323 switch (u.format) { | |
324 case 1: return u.format1.sanitize (c); | |
325 default:return true; | |
326 } | |
327 } | |
328 | |
329 private: | |
330 union { | |
331 USHORT format; /* Format identifier */ | |
332 AlternateSubstFormat1 format1; | |
333 } u; | |
334 }; | |
335 | |
336 | |
337 struct Ligature | |
338 { | |
339 friend struct LigatureSet; | |
340 | |
341 private: | |
342 inline bool apply (hb_apply_context_t *c) const | |
343 { | |
344 TRACE_APPLY (); | |
345 unsigned int i, j; | |
346 unsigned int count = component.len; | |
347 unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length); | |
348 if (unlikely (count < 2 || c->buffer->i + count > end)) | |
349 return false; | |
350 | |
351 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); | |
352 bool found_non_mark = false; | |
353 | |
354 for (i = 1, j = c->buffer->i + 1; i < count; i++, j++) | |
355 { | |
356 unsigned int property; | |
357 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->l
ookup_props, &property)) | |
358 { | |
359 if (unlikely (j + count - i == end)) | |
360 return false; | |
361 j++; | |
362 } | |
363 | |
364 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); | |
365 | |
366 if (likely (c->buffer->info[j].codepoint != component[i])) | |
367 return false; | |
368 } | |
369 | |
370 if (first_was_mark && found_non_mark) | |
371 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE); | |
372 | |
373 /* Allocate new ligature id */ | |
374 unsigned int lig_id = allocate_lig_id (c->buffer); | |
375 c->buffer->info[c->buffer->i].lig_comp() = 0; | |
376 c->buffer->info[c->buffer->i].lig_id() = lig_id; | |
377 | |
378 if (j == c->buffer->i + i) /* No input glyphs skipped */ | |
379 { | |
380 c->replace_glyphs_be16 (i, 1, (const uint16_t *) &ligGlyph); | |
381 } | |
382 else | |
383 { | |
384 c->replace_glyph (ligGlyph); | |
385 | |
386 /* Now we must do a second loop to copy the skipped glyphs to | |
387 `out' and assign component values to it. We start with the | |
388 glyph after the first component. Glyphs between component | |
389 i and i+1 belong to component i. Together with the lig_id | |
390 value it is later possible to check whether a specific | |
391 component value really belongs to a given ligature. */ | |
392 | |
393 for (i = 1; i < count; i++) | |
394 { | |
395 while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buf
fer->i], c->lookup_props, NULL)) | |
396 { | |
397 c->buffer->info[c->buffer->i].lig_comp() = i; | |
398 c->buffer->info[c->buffer->i].lig_id() = lig_id; | |
399 c->replace_glyph (c->buffer->info[c->buffer->i].codepoint); | |
400 } | |
401 | |
402 /* Skip the base glyph */ | |
403 c->buffer->i++; | |
404 } | |
405 } | |
406 | |
407 return true; | |
408 } | |
409 | |
410 inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const { | |
411 uint16_t lig_id = buffer->next_serial (); | |
412 if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overf
lows */ | |
413 return lig_id; | |
414 } | |
415 | |
416 public: | |
417 inline bool sanitize (hb_sanitize_context_t *c) { | |
418 TRACE_SANITIZE (); | |
419 return ligGlyph.sanitize (c) | |
420 && component.sanitize (c); | |
421 } | |
422 | |
423 private: | |
424 GlyphID ligGlyph; /* GlyphID of ligature to substitute */ | |
425 HeadlessArrayOf<GlyphID> | |
426 component; /* Array of component GlyphIDs--start | |
427 * with the second component--ordered | |
428 * in writing direction */ | |
429 public: | |
430 DEFINE_SIZE_ARRAY (4, component); | |
431 }; | |
432 | |
433 struct LigatureSet | |
434 { | |
435 friend struct LigatureSubstFormat1; | |
436 | |
437 private: | |
438 inline bool apply (hb_apply_context_t *c) const | |
439 { | |
440 TRACE_APPLY (); | |
441 unsigned int num_ligs = ligature.len; | |
442 for (unsigned int i = 0; i < num_ligs; i++) | |
443 { | |
444 const Ligature &lig = this+ligature[i]; | |
445 if (lig.apply (c)) | |
446 return true; | |
447 } | |
448 | |
449 return false; | |
450 } | |
451 | |
452 public: | |
453 inline bool sanitize (hb_sanitize_context_t *c) { | |
454 TRACE_SANITIZE (); | |
455 return ligature.sanitize (c, this); | |
456 } | |
457 | |
458 private: | |
459 OffsetArrayOf<Ligature> | |
460 ligature; /* Array LigatureSet tables | |
461 * ordered by preference */ | |
462 public: | |
463 DEFINE_SIZE_ARRAY (2, ligature); | |
464 }; | |
465 | |
466 struct LigatureSubstFormat1 | |
467 { | |
468 friend struct LigatureSubst; | |
469 | |
470 private: | |
471 inline bool apply (hb_apply_context_t *c) const | |
472 { | |
473 TRACE_APPLY (); | |
474 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint; | |
475 | |
476 unsigned int index = (this+coverage) (glyph_id); | |
477 if (likely (index == NOT_COVERED)) | |
478 return false; | |
479 | |
480 const LigatureSet &lig_set = this+ligatureSet[index]; | |
481 return lig_set.apply (c); | |
482 } | |
483 | |
484 inline bool sanitize (hb_sanitize_context_t *c) { | |
485 TRACE_SANITIZE (); | |
486 return coverage.sanitize (c, this) | |
487 && ligatureSet.sanitize (c, this); | |
488 } | |
489 | |
490 private: | |
491 USHORT format; /* Format identifier--format = 1 */ | |
492 OffsetTo<Coverage> | |
493 coverage; /* Offset to Coverage table--from | |
494 * beginning of Substitution table */ | |
495 OffsetArrayOf<LigatureSet> | |
496 ligatureSet; /* Array LigatureSet tables | |
497 * ordered by Coverage Index */ | |
498 public: | |
499 DEFINE_SIZE_ARRAY (6, ligatureSet); | |
500 }; | |
501 | |
502 struct LigatureSubst | |
503 { | |
504 friend struct SubstLookupSubTable; | |
505 | |
506 private: | |
507 inline bool apply (hb_apply_context_t *c) const | |
508 { | |
509 TRACE_APPLY (); | |
510 switch (u.format) { | |
511 case 1: return u.format1.apply (c); | |
512 default:return false; | |
513 } | |
514 } | |
515 | |
516 inline bool sanitize (hb_sanitize_context_t *c) { | |
517 TRACE_SANITIZE (); | |
518 if (!u.format.sanitize (c)) return false; | |
519 switch (u.format) { | |
520 case 1: return u.format1.sanitize (c); | |
521 default:return true; | |
522 } | |
523 } | |
524 | |
525 private: | |
526 union { | |
527 USHORT format; /* Format identifier */ | |
528 LigatureSubstFormat1 format1; | |
529 } u; | |
530 }; | |
531 | |
532 | |
533 HB_BEGIN_DECLS | |
534 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup
_index); | |
535 HB_END_DECLS | |
536 | |
537 struct ContextSubst : Context | |
538 { | |
539 friend struct SubstLookupSubTable; | |
540 | |
541 private: | |
542 inline bool apply (hb_apply_context_t *c) const | |
543 { | |
544 TRACE_APPLY (); | |
545 return Context::apply (c, substitute_lookup); | |
546 } | |
547 }; | |
548 | |
549 struct ChainContextSubst : ChainContext | |
550 { | |
551 friend struct SubstLookupSubTable; | |
552 | |
553 private: | |
554 inline bool apply (hb_apply_context_t *c) const | |
555 { | |
556 TRACE_APPLY (); | |
557 return ChainContext::apply (c, substitute_lookup); | |
558 } | |
559 }; | |
560 | |
561 | |
562 struct ExtensionSubst : Extension | |
563 { | |
564 friend struct SubstLookupSubTable; | |
565 friend struct SubstLookup; | |
566 | |
567 private: | |
568 inline const struct SubstLookupSubTable& get_subtable (void) const | |
569 { | |
570 unsigned int offset = get_offset (); | |
571 if (unlikely (!offset)) return Null(SubstLookupSubTable); | |
572 return StructAtOffset<SubstLookupSubTable> (this, offset); | |
573 } | |
574 | |
575 inline bool apply (hb_apply_context_t *c) const; | |
576 | |
577 inline bool sanitize (hb_sanitize_context_t *c); | |
578 | |
579 inline bool is_reverse (void) const; | |
580 }; | |
581 | |
582 | |
583 struct ReverseChainSingleSubstFormat1 | |
584 { | |
585 friend struct ReverseChainSingleSubst; | |
586 | |
587 private: | |
588 inline bool apply (hb_apply_context_t *c) const | |
589 { | |
590 TRACE_APPLY (); | |
591 if (unlikely (c->context_length != NO_CONTEXT)) | |
592 return false; /* No chaining to this type */ | |
593 | |
594 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoin
t); | |
595 if (likely (index == NOT_COVERED)) | |
596 return false; | |
597 | |
598 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverag
e> > (backtrack); | |
599 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahe
ad); | |
600 | |
601 if (match_backtrack (c, | |
602 backtrack.len, (USHORT *) backtrack.array, | |
603 match_coverage, this) && | |
604 match_lookahead (c, | |
605 lookahead.len, (USHORT *) lookahead.array, | |
606 match_coverage, this, | |
607 1)) | |
608 { | |
609 c->buffer->info[c->buffer->i].codepoint = substitute[index]; | |
610 c->buffer->i--; /* Reverse! */ | |
611 return true; | |
612 } | |
613 | |
614 return false; | |
615 } | |
616 | |
617 inline bool sanitize (hb_sanitize_context_t *c) { | |
618 TRACE_SANITIZE (); | |
619 if (!(coverage.sanitize (c, this) | |
620 && backtrack.sanitize (c, this))) | |
621 return false; | |
622 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (
backtrack); | |
623 if (!lookahead.sanitize (c, this)) | |
624 return false; | |
625 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); | |
626 return substitute.sanitize (c); | |
627 } | |
628 | |
629 private: | |
630 USHORT format; /* Format identifier--format = 1 */ | |
631 OffsetTo<Coverage> | |
632 coverage; /* Offset to Coverage table--from | |
633 * beginning of table */ | |
634 OffsetArrayOf<Coverage> | |
635 backtrack; /* Array of coverage tables | |
636 * in backtracking sequence, in glyph | |
637 * sequence order */ | |
638 OffsetArrayOf<Coverage> | |
639 lookaheadX; /* Array of coverage tables | |
640 * in lookahead sequence, in glyph | |
641 * sequence order */ | |
642 ArrayOf<GlyphID> | |
643 substituteX; /* Array of substitute | |
644 * GlyphIDs--ordered by Coverage Index *
/ | |
645 public: | |
646 DEFINE_SIZE_MIN (10); | |
647 }; | |
648 | |
649 struct ReverseChainSingleSubst | |
650 { | |
651 friend struct SubstLookupSubTable; | |
652 | |
653 private: | |
654 inline bool apply (hb_apply_context_t *c) const | |
655 { | |
656 TRACE_APPLY (); | |
657 switch (u.format) { | |
658 case 1: return u.format1.apply (c); | |
659 default:return false; | |
660 } | |
661 } | |
662 | |
663 inline bool sanitize (hb_sanitize_context_t *c) { | |
664 TRACE_SANITIZE (); | |
665 if (!u.format.sanitize (c)) return false; | |
666 switch (u.format) { | |
667 case 1: return u.format1.sanitize (c); | |
668 default:return true; | |
669 } | |
670 } | |
671 | |
672 private: | |
673 union { | |
674 USHORT format; /* Format identifier */ | |
675 ReverseChainSingleSubstFormat1 format1; | |
676 } u; | |
677 }; | |
678 | |
679 | |
680 | |
681 /* | |
682 * SubstLookup | |
683 */ | |
684 | |
685 struct SubstLookupSubTable | |
686 { | |
687 friend struct SubstLookup; | |
688 | |
689 enum { | |
690 Single = 1, | |
691 Multiple = 2, | |
692 Alternate = 3, | |
693 Ligature = 4, | |
694 Context = 5, | |
695 ChainContext = 6, | |
696 Extension = 7, | |
697 ReverseChainSingle = 8 | |
698 }; | |
699 | |
700 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const | |
701 { | |
702 TRACE_APPLY (); | |
703 switch (lookup_type) { | |
704 case Single: return u.single.apply (c); | |
705 case Multiple: return u.multiple.apply (c); | |
706 case Alternate: return u.alternate.apply (c); | |
707 case Ligature: return u.ligature.apply (c); | |
708 case Context: return u.c.apply (c); | |
709 case ChainContext: return u.chainContext.apply (c); | |
710 case Extension: return u.extension.apply (c); | |
711 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c); | |
712 default:return false; | |
713 } | |
714 } | |
715 | |
716 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { | |
717 TRACE_SANITIZE (); | |
718 switch (lookup_type) { | |
719 case Single: return u.single.sanitize (c); | |
720 case Multiple: return u.multiple.sanitize (c); | |
721 case Alternate: return u.alternate.sanitize (c); | |
722 case Ligature: return u.ligature.sanitize (c); | |
723 case Context: return u.c.sanitize (c); | |
724 case ChainContext: return u.chainContext.sanitize (c); | |
725 case Extension: return u.extension.sanitize (c); | |
726 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c); | |
727 default:return true; | |
728 } | |
729 } | |
730 | |
731 private: | |
732 union { | |
733 USHORT sub_format; | |
734 SingleSubst single; | |
735 MultipleSubst multiple; | |
736 AlternateSubst alternate; | |
737 LigatureSubst ligature; | |
738 ContextSubst c; | |
739 ChainContextSubst chainContext; | |
740 ExtensionSubst extension; | |
741 ReverseChainSingleSubst reverseChainContextSingle; | |
742 } u; | |
743 public: | |
744 DEFINE_SIZE_UNION (2, sub_format); | |
745 }; | |
746 | |
747 | |
748 struct SubstLookup : Lookup | |
749 { | |
750 inline const SubstLookupSubTable& get_subtable (unsigned int i) const | |
751 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } | |
752 | |
753 inline static bool lookup_type_is_reverse (unsigned int lookup_type) | |
754 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } | |
755 | |
756 inline bool is_reverse (void) const | |
757 { | |
758 unsigned int type = get_type (); | |
759 if (unlikely (type == SubstLookupSubTable::Extension)) | |
760 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse (); | |
761 return lookup_type_is_reverse (type); | |
762 } | |
763 | |
764 | |
765 inline bool apply_once (hb_ot_layout_context_t *layout, | |
766 hb_buffer_t *buffer, | |
767 hb_mask_t lookup_mask, | |
768 unsigned int context_length, | |
769 unsigned int nesting_level_left) const | |
770 { | |
771 unsigned int lookup_type = get_type (); | |
772 hb_apply_context_t c[1] = {{0}}; | |
773 | |
774 c->layout = layout; | |
775 c->buffer = buffer; | |
776 c->lookup_mask = lookup_mask; | |
777 c->context_length = context_length; | |
778 c->nesting_level_left = nesting_level_left; | |
779 c->lookup_props = get_props (); | |
780 | |
781 if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c
->buffer->i], c->lookup_props, &c->property)) | |
782 return false; | |
783 | |
784 if (unlikely (lookup_type == SubstLookupSubTable::Extension)) | |
785 { | |
786 /* The spec says all subtables should have the same type. | |
787 * This is specially important if one has a reverse type! | |
788 * | |
789 * This is rather slow to do this here for every glyph, | |
790 * but it's easiest, and who uses extension lookups anyway?!*/ | |
791 unsigned int count = get_subtable_count (); | |
792 unsigned int type = get_subtable(0).u.extension.get_type (); | |
793 for (unsigned int i = 1; i < count; i++) | |
794 if (get_subtable(i).u.extension.get_type () != type) | |
795 return false; | |
796 } | |
797 | |
798 unsigned int count = get_subtable_count (); | |
799 for (unsigned int i = 0; i < count; i++) | |
800 if (get_subtable (i).apply (c, lookup_type)) | |
801 return true; | |
802 | |
803 return false; | |
804 } | |
805 | |
806 inline bool apply_string (hb_ot_layout_context_t *layout, | |
807 hb_buffer_t *buffer, | |
808 hb_mask_t mask) const | |
809 { | |
810 bool ret = false; | |
811 | |
812 if (unlikely (!buffer->len)) | |
813 return false; | |
814 | |
815 if (likely (!is_reverse ())) | |
816 { | |
817 /* in/out forward substitution */ | |
818 buffer->clear_output (); | |
819 buffer->i = 0; | |
820 while (buffer->i < buffer->len) | |
821 { | |
822 if ((buffer->info[buffer->i].mask & mask) && | |
823 apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) | |
824 ret = true; | |
825 else | |
826 buffer->next_glyph (); | |
827 | |
828 } | |
829 if (ret) | |
830 buffer->swap (); | |
831 } | |
832 else | |
833 { | |
834 /* in-place backward substitution */ | |
835 buffer->i = buffer->len - 1; | |
836 do | |
837 { | |
838 if ((buffer->info[buffer->i].mask & mask) && | |
839 apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) | |
840 ret = true; | |
841 else | |
842 buffer->i--; | |
843 | |
844 } | |
845 while ((int) buffer->i >= 0); | |
846 } | |
847 | |
848 return ret; | |
849 } | |
850 | |
851 inline bool sanitize (hb_sanitize_context_t *c) { | |
852 TRACE_SANITIZE (); | |
853 if (unlikely (!Lookup::sanitize (c))) return false; | |
854 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSu
bTable> > (subTable); | |
855 return list.sanitize (c, this, get_type ()); | |
856 } | |
857 }; | |
858 | |
859 typedef OffsetListOf<SubstLookup> SubstLookupList; | |
860 | |
861 /* | |
862 * GSUB | |
863 */ | |
864 | |
865 struct GSUB : GSUBGPOS | |
866 { | |
867 static const hb_tag_t Tag = HB_OT_TAG_GSUB; | |
868 | |
869 inline const SubstLookup& get_lookup (unsigned int i) const | |
870 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } | |
871 | |
872 inline bool substitute_lookup (hb_ot_layout_context_t *layout, | |
873 hb_buffer_t *buffer, | |
874 unsigned int lookup_index, | |
875 hb_mask_t mask) const | |
876 { return get_lookup (lookup_index).apply_string (layout, buffer, mask); } | |
877 | |
878 inline bool sanitize (hb_sanitize_context_t *c) { | |
879 TRACE_SANITIZE (); | |
880 if (unlikely (!GSUBGPOS::sanitize (c))) return false; | |
881 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupL
ist); | |
882 return list.sanitize (c, this); | |
883 } | |
884 public: | |
885 DEFINE_SIZE_STATIC (10); | |
886 }; | |
887 | |
888 | |
889 /* Out-of-class implementation for methods recursing */ | |
890 | |
891 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const | |
892 { | |
893 TRACE_APPLY (); | |
894 return get_subtable ().apply (c, get_type ()); | |
895 } | |
896 | |
897 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) | |
898 { | |
899 TRACE_SANITIZE (); | |
900 if (unlikely (!Extension::sanitize (c))) return false; | |
901 unsigned int offset = get_offset (); | |
902 if (unlikely (!offset)) return true; | |
903 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_typ
e ()); | |
904 } | |
905 | |
906 inline bool ExtensionSubst::is_reverse (void) const | |
907 { | |
908 unsigned int type = get_type (); | |
909 if (unlikely (type == SubstLookupSubTable::Extension)) | |
910 return CastR<ExtensionSubst> (get_subtable()).is_reverse (); | |
911 return SubstLookup::lookup_type_is_reverse (type); | |
912 } | |
913 | |
914 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup
_index) | |
915 { | |
916 const GSUB &gsub = *(c->layout->face->ot_layout->gsub); | |
917 const SubstLookup &l = gsub.get_lookup (lookup_index); | |
918 | |
919 if (unlikely (c->nesting_level_left == 0)) | |
920 return false; | |
921 | |
922 if (unlikely (c->context_length < 1)) | |
923 return false; | |
924 | |
925 return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length,
c->nesting_level_left - 1); | |
926 } | |
927 | |
928 | |
929 HB_END_DECLS | |
930 | |
931 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */ | |
OLD | NEW |