Coverage Report

Created: 2026-08-14 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
Line
Count
Source
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
23
  bool sanitize (hb_sanitize_context_t *c) const
24
1.04k
  {
25
1.04k
    TRACE_SANITIZE (this);
26
1.04k
    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
27
1.04k
  }
28
29
  bool intersects (const hb_set_t *glyphs) const
30
0
  { return hb_all (component, glyphs); }
31
32
  bool intersects_lig_glyph (const hb_set_t *glyphs) const
33
0
  { return glyphs->has(ligGlyph); }
34
35
  void closure (hb_closure_context_t *c) const
36
0
  {
37
0
    if (!intersects (c->glyphs)) return;
38
0
    c->output->add (ligGlyph);
39
0
  }
40
41
  void depend (hb_depend_context_t *c, hb_codepoint_t first) const
42
0
  {
43
0
    // Build the complete ligature set upfront before adding any edges
44
0
    hb_set_t complete_ligset;
45
0
    complete_ligset.add (first);
46
0
    + hb_iter (component) | hb_sink (complete_ligset);
47
0
    if (unlikely (complete_ligset.in_error ()))
48
0
    {
49
0
      c->depend_data->fail ();
50
0
      return;
51
0
    }
52
0
53
0
    hb_codepoint_t ligset_idx = c->depend_data->new_ligature_set(complete_ligset);
54
0
    if (unlikely (ligset_idx == HB_CODEPOINT_INVALID))
55
0
      return;
56
0
57
0
    // Track whether any edge using this ligset_idx was actually added
58
0
    bool any_added = false;
59
0
60
0
    // Now add all edges with the complete, immutable set
61
0
    if (c->depend_data->add_gsub_lookup (first, c->lookup_index, ligGlyph, ligset_idx))
62
0
      any_added = true;
63
0
64
0
    + hb_iter (component)
65
0
    | hb_apply ([&] (const hb_codepoint_t &gid) {
66
0
        if (c->depend_data->add_gsub_lookup (gid, c->lookup_index, ligGlyph, ligset_idx))
67
0
          any_added = true;
68
0
      })
69
0
    ;
70
0
71
0
    // If no edges were added, the ligset_idx is unused - free it for reuse
72
0
    if (!any_added)
73
0
      c->depend_data->free_ligature_set(ligset_idx);
74
0
  }
75
76
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
77
0
  {
78
0
    c->input->add_array (component.arrayZ, component.get_length ());
79
0
    c->output->add (ligGlyph);
80
0
  }
81
82
  template <typename set_t>
83
  void collect_second (set_t &s) const
84
57
  {
85
57
    if (unlikely (!component.get_length ()))
86
0
    {
87
      // A ligature without any components. Anything matches.
88
0
      s = set_t::full ();
89
0
      return;
90
0
    }
91
57
    s.add (component.arrayZ[0]);
92
57
  }
93
94
  bool would_apply (hb_would_apply_context_t *c) const
95
0
  {
96
0
    if (c->len != component.lenP1)
97
0
      return false;
98
99
0
    for (unsigned int i = 1; i < c->len; i++)
100
0
      if (likely (c->glyphs[i] != component[i]))
101
0
        return false;
102
103
0
    return true;
104
0
  }
105
106
  bool apply (hb_ot_apply_context_t *c) const
107
0
  {
108
0
    TRACE_APPLY (this);
109
0
    unsigned int count = component.lenP1;
110
111
0
    if (unlikely (!count)) return_trace (false);
112
113
    /* Special-case to make it in-place and not consider this
114
     * as a "ligated" substitution. */
115
0
    if (unlikely (count == 1))
116
0
    {
117
118
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
119
0
      {
120
0
  c->buffer->sync_so_far ();
121
0
  c->buffer->message (c->font,
122
0
          "replacing glyph at %u (ligature substitution)",
123
0
          c->buffer->idx);
124
0
      }
125
126
0
      c->replace_glyph (ligGlyph);
127
128
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
129
0
      {
130
0
  c->buffer->message (c->font,
131
0
          "replaced glyph at %u (ligature substitution)",
132
0
          c->buffer->idx - 1u);
133
0
      }
134
135
0
      return_trace (true);
136
0
    }
137
138
0
    unsigned int total_component_count = 0;
139
140
0
    if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
141
0
    unsigned int match_end = 0;
142
143
0
    if (likely (!match_input (c, count,
144
0
                              &component[1],
145
0
                              match_glyph,
146
0
                              nullptr,
147
0
                              &match_end,
148
0
                              &total_component_count)))
149
0
    {
150
0
      c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
151
0
      return_trace (false);
152
0
    }
153
154
0
    unsigned pos = 0;
155
0
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
156
0
    {
157
0
      unsigned delta = c->buffer->sync_so_far ();
158
159
0
      pos = c->buffer->idx;
160
161
0
      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
162
0
      char *p = buf;
163
164
0
      match_end += delta;
165
0
      for (unsigned i = 0; i < count; i++)
166
0
      {
167
0
  c->match_positions[i] += delta;
168
0
  if (i)
169
0
    *p++ = ',';
170
0
  snprintf (p, sizeof(buf) - (p - buf), "%u", c->match_positions[i]);
171
0
  p += strlen(p);
172
0
      }
173
174
0
      c->buffer->message (c->font,
175
0
        "ligating glyphs at %s",
176
0
        buf);
177
0
    }
178
179
0
    ligate_input (c,
180
0
                  count,
181
0
                  match_end,
182
0
                  ligGlyph,
183
0
                  total_component_count);
184
185
0
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
186
0
    {
187
0
      c->buffer->sync_so_far ();
188
0
      c->buffer->message (c->font,
189
0
        "ligated glyph at %u",
190
0
        pos);
191
0
    }
192
193
0
    return_trace (true);
194
0
  }
195
196
  template <typename Iterator,
197
            hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
198
  bool serialize (hb_serialize_context_t *c,
199
                  hb_codepoint_t ligature,
200
                  Iterator components /* Starting from second */)
201
0
  {
202
0
    TRACE_SERIALIZE (this);
203
0
    if (unlikely (!c->extend_min (this))) return_trace (false);
204
0
    ligGlyph = ligature;
205
0
    if (unlikely (!component.serialize (c, components))) return_trace (false);
206
0
    return_trace (true);
207
0
  }
Unexecuted instantiation: _ZN2OT6Layout9GSUB_impl8LigatureINS0_10SmallTypesEE9serializeI10hb_array_tIKNS_11HBGlyphID16EETnPN12hb_enable_ifIXsr15hb_is_source_ofIT_jEE5valueEvE4typeELPv0EEEbP22hb_serialize_context_tjSB_
Unexecuted instantiation: _ZN2OT6Layout9GSUB_impl8LigatureINS0_10SmallTypesEE9serializeI13hb_map_iter_tI10hb_array_tIKNS_11HBGlyphID16EERK8hb_map_tL24hb_function_sortedness_t0ELPv0EETnPN12hb_enable_ifIXsr15hb_is_source_ofIT_jEE5valueEvE4typeELSF_0EEEbP22hb_serialize_context_tjSI_
208
209
  bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
210
0
  {
211
0
    TRACE_SUBSET (this);
212
0
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
213
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
214
0
215
0
    if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
216
0
    // Ensure Coverage table is always packed after this.
217
0
    c->serializer->add_virtual_link (coverage_idx);
218
0
219
0
    auto it =
220
0
      + hb_iter (component)
221
0
      | hb_map (glyph_map)
222
0
      ;
223
0
224
0
    auto *out = c->serializer->start_embed (*this);
225
0
    return_trace (out->serialize (c->serializer,
226
0
                                  glyph_map[ligGlyph],
227
0
                                  it));  }
228
};
229
230
231
}
232
}
233
}
234
235
#endif  /* OT_LAYOUT_GSUB_LIGATURE_HH */