xref: /aosp_15_r20/external/harfbuzz_ng/src/OT/Layout/GSUB/Ligature.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 #ifndef OT_LAYOUT_GSUB_LIGATURE_HH
2 #define OT_LAYOUT_GSUB_LIGATURE_HH
3 
4 #include "Common.hh"
5 
6 namespace OT {
7 namespace Layout {
8 namespace GSUB_impl {
9 
10 template <typename Types>
11 struct Ligature
12 {
13   public:
14   typename Types::HBGlyphID
15 		ligGlyph;               /* GlyphID of ligature to substitute */
16   HeadlessArray16Of<typename Types::HBGlyphID>
17 		component;              /* Array of component GlyphIDs--start
18                                          * with the second  component--ordered
19                                          * in writing direction */
20   public:
21   DEFINE_SIZE_ARRAY (Types::size + 2, component);
22 
sanitizeOT::Layout::GSUB_impl::Ligature23   bool sanitize (hb_sanitize_context_t *c) const
24   {
25     TRACE_SANITIZE (this);
26     return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
27   }
28 
intersectsOT::Layout::GSUB_impl::Ligature29   bool intersects (const hb_set_t *glyphs) const
30   { return hb_all (component, glyphs); }
31 
intersects_lig_glyphOT::Layout::GSUB_impl::Ligature32   bool intersects_lig_glyph (const hb_set_t *glyphs) const
33   { return glyphs->has(ligGlyph); }
34 
closureOT::Layout::GSUB_impl::Ligature35   void closure (hb_closure_context_t *c) const
36   {
37     if (!intersects (c->glyphs)) return;
38     c->output->add (ligGlyph);
39   }
40 
collect_glyphsOT::Layout::GSUB_impl::Ligature41   void collect_glyphs (hb_collect_glyphs_context_t *c) const
42   {
43     c->input->add_array (component.arrayZ, component.get_length ());
44     c->output->add (ligGlyph);
45   }
46 
would_applyOT::Layout::GSUB_impl::Ligature47   bool would_apply (hb_would_apply_context_t *c) const
48   {
49     if (c->len != component.lenP1)
50       return false;
51 
52     for (unsigned int i = 1; i < c->len; i++)
53       if (likely (c->glyphs[i] != component[i]))
54         return false;
55 
56     return true;
57   }
58 
applyOT::Layout::GSUB_impl::Ligature59   bool apply (hb_ot_apply_context_t *c) const
60   {
61     TRACE_APPLY (this);
62     unsigned int count = component.lenP1;
63 
64     if (unlikely (!count)) return_trace (false);
65 
66     /* Special-case to make it in-place and not consider this
67      * as a "ligated" substitution. */
68     if (unlikely (count == 1))
69     {
70 
71       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
72       {
73 	c->buffer->sync_so_far ();
74 	c->buffer->message (c->font,
75 			    "replacing glyph at %u (ligature substitution)",
76 			    c->buffer->idx);
77       }
78 
79       c->replace_glyph (ligGlyph);
80 
81       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
82       {
83 	c->buffer->message (c->font,
84 			    "replaced glyph at %u (ligature substitution)",
85 			    c->buffer->idx - 1u);
86       }
87 
88       return_trace (true);
89     }
90 
91     unsigned int total_component_count = 0;
92 
93     if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
94     unsigned match_positions_stack[4];
95     unsigned *match_positions = match_positions_stack;
96     if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
97     {
98       match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
99       if (unlikely (!match_positions))
100 	return_trace (false);
101     }
102 
103     unsigned int match_end = 0;
104 
105     if (likely (!match_input (c, count,
106                               &component[1],
107                               match_glyph,
108                               nullptr,
109                               &match_end,
110                               match_positions,
111                               &total_component_count)))
112     {
113       c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
114       if (match_positions != match_positions_stack)
115         hb_free (match_positions);
116       return_trace (false);
117     }
118 
119     unsigned pos = 0;
120     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
121     {
122       unsigned delta = c->buffer->sync_so_far ();
123 
124       pos = c->buffer->idx;
125 
126       char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
127       char *p = buf;
128 
129       match_end += delta;
130       for (unsigned i = 0; i < count; i++)
131       {
132 	match_positions[i] += delta;
133 	if (i)
134 	  *p++ = ',';
135 	snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
136 	p += strlen(p);
137       }
138 
139       c->buffer->message (c->font,
140 			  "ligating glyphs at %s",
141 			  buf);
142     }
143 
144     ligate_input (c,
145                   count,
146                   match_positions,
147                   match_end,
148                   ligGlyph,
149                   total_component_count);
150 
151     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
152     {
153       c->buffer->sync_so_far ();
154       c->buffer->message (c->font,
155 			  "ligated glyph at %u",
156 			  pos);
157     }
158 
159     if (match_positions != match_positions_stack)
160       hb_free (match_positions);
161     return_trace (true);
162   }
163 
164   template <typename Iterator,
165             hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
serializeOT::Layout::GSUB_impl::Ligature166   bool serialize (hb_serialize_context_t *c,
167                   hb_codepoint_t ligature,
168                   Iterator components /* Starting from second */)
169   {
170     TRACE_SERIALIZE (this);
171     if (unlikely (!c->extend_min (this))) return_trace (false);
172     ligGlyph = ligature;
173     if (unlikely (!component.serialize (c, components))) return_trace (false);
174     return_trace (true);
175   }
176 
subsetOT::Layout::GSUB_impl::Ligature177   bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
178   {
179     TRACE_SUBSET (this);
180     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
181     const hb_map_t &glyph_map = *c->plan->glyph_map;
182 
183     if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
184     // Ensure Coverage table is always packed after this.
185     c->serializer->add_virtual_link (coverage_idx);
186 
187     auto it =
188       + hb_iter (component)
189       | hb_map (glyph_map)
190       ;
191 
192     auto *out = c->serializer->start_embed (*this);
193     return_trace (out->serialize (c->serializer,
194                                   glyph_map[ligGlyph],
195                                   it));  }
196 };
197 
198 
199 }
200 }
201 }
202 
203 #endif  /* OT_LAYOUT_GSUB_LIGATURE_HH */
204