1#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
2#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
3
4#include "Common.hh"
5#include "LigatureSet.hh"
6
7namespace OT {
8namespace Layout {
9namespace GSUB_impl {
10
11template <typename Types>
12struct LigatureSubstFormat1_2
13{
14 protected:
15 HBUINT16 format; /* Format identifier--format = 1 */
16 typename Types::template OffsetTo<Coverage>
17 coverage; /* Offset to Coverage table--from
18 * beginning of Substitution table */
19 Array16Of<typename Types::template OffsetTo<LigatureSet<Types>>>
20 ligatureSet; /* Array LigatureSet tables
21 * ordered by Coverage Index */
22 public:
23 DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet);
24
25 bool sanitize (hb_sanitize_context_t *c) const
26 {
27 TRACE_SANITIZE (this);
28 return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
29 }
30
31 bool intersects (const hb_set_t *glyphs) const
32 {
33 return
34 + hb_zip (this+coverage, ligatureSet)
35 | hb_filter (*glyphs, hb_first)
36 | hb_map (hb_second)
37 | hb_map ([this, glyphs] (const typename Types::template OffsetTo<LigatureSet<Types>> &_)
38 { return (this+_).intersects (glyphs); })
39 | hb_any
40 ;
41 }
42
43 bool may_have_non_1to1 () const
44 { return true; }
45
46 void closure (hb_closure_context_t *c) const
47 {
48 + hb_zip (this+coverage, ligatureSet)
49 | hb_filter (c->parent_active_glyphs (), hb_first)
50 | hb_map (hb_second)
51 | hb_map (hb_add (this))
52 | hb_apply ([c] (const LigatureSet<Types> &_) { _.closure (c); })
53 ;
54
55 }
56
57 void closure_lookups (hb_closure_lookups_context_t *c) const {}
58
59 void collect_glyphs (hb_collect_glyphs_context_t *c) const
60 {
61 if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
62
63 + hb_zip (this+coverage, ligatureSet)
64 | hb_map (hb_second)
65 | hb_map (hb_add (this))
66 | hb_apply ([c] (const LigatureSet<Types> &_) { _.collect_glyphs (c); })
67 ;
68 }
69
70 const Coverage &get_coverage () const { return this+coverage; }
71
72 bool would_apply (hb_would_apply_context_t *c) const
73 {
74 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
75 if (likely (index == NOT_COVERED)) return false;
76
77 const auto &lig_set = this+ligatureSet[index];
78 return lig_set.would_apply (c);
79 }
80
81 bool apply (hb_ot_apply_context_t *c) const
82 {
83 TRACE_APPLY (this);
84
85 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
86 if (likely (index == NOT_COVERED)) return_trace (false);
87
88 const auto &lig_set = this+ligatureSet[index];
89 return_trace (lig_set.apply (c));
90 }
91
92 bool serialize (hb_serialize_context_t *c,
93 hb_sorted_array_t<const HBGlyphID16> first_glyphs,
94 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
95 hb_array_t<const HBGlyphID16> ligatures_list,
96 hb_array_t<const unsigned int> component_count_list,
97 hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
98 {
99 TRACE_SERIALIZE (this);
100 if (unlikely (!c->extend_min (this))) return_trace (false);
101 if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
102 for (unsigned int i = 0; i < first_glyphs.length; i++)
103 {
104 unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
105 if (unlikely (!ligatureSet[i]
106 .serialize_serialize (c,
107 ligatures_list.sub_array (0, ligature_count),
108 component_count_list.sub_array (0, ligature_count),
109 component_list))) return_trace (false);
110 ligatures_list += ligature_count;
111 component_count_list += ligature_count;
112 }
113 return_trace (coverage.serialize_serialize (c, first_glyphs));
114 }
115
116 bool subset (hb_subset_context_t *c) const
117 {
118 TRACE_SUBSET (this);
119 const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
120 const hb_map_t &glyph_map = *c->plan->glyph_map;
121
122 auto *out = c->serializer->start_embed (*this);
123 if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
124 out->format = format;
125
126 // Due to a bug in some older versions of windows 7 the Coverage table must be
127 // packed after the LigatureSet and Ligature tables, so serialize Coverage first
128 // which places it last in the packed order.
129 hb_set_t new_coverage;
130 + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
131 | hb_filter (glyphset, hb_first)
132 | hb_filter ([&] (const LigatureSet<Types>& _) {
133 return _.intersects_lig_glyph (&glyphset);
134 }, hb_second)
135 | hb_map (hb_first)
136 | hb_sink (new_coverage);
137
138 if (!c->serializer->push<Coverage> ()
139 ->serialize (c->serializer,
140 + new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
141 {
142 c->serializer->pop_discard ();
143 return_trace (false);
144 }
145
146 unsigned coverage_idx = c->serializer->pop_pack ();
147 c->serializer->add_link (out->coverage, coverage_idx);
148
149 + hb_zip (this+coverage, ligatureSet)
150 | hb_filter (new_coverage, hb_first)
151 | hb_map (hb_second)
152 // to ensure that the repacker always orders the coverage table after the LigatureSet
153 // and LigatureSubtable's they will be linked to the Coverage table via a virtual link
154 // the coverage table object idx is passed down to facilitate this.
155 | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
156 ;
157
158 return_trace (bool (new_coverage));
159 }
160};
161
162}
163}
164}
165
166#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */
167