Coverage Report

Created: 2025-07-23 07:07

/src/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
Line
Count
Source (jump to first uncovered line)
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.30M
  {
25
1.30M
    TRACE_SANITIZE (this);
26
1.30M
    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
27
1.30M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::sanitize(hb_sanitize_context_t*) const
Line
Count
Source
24
1.13M
  {
25
1.13M
    TRACE_SANITIZE (this);
26
1.13M
    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
27
1.13M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::sanitize(hb_sanitize_context_t*) const
Line
Count
Source
24
169k
  {
25
169k
    TRACE_SANITIZE (this);
26
169k
    return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
27
169k
  }
28
29
  bool intersects (const hb_set_t *glyphs) const
30
1.39M
  { return hb_all (component, glyphs); }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::intersects(hb_set_t const*) const
Line
Count
Source
30
1.32M
  { return hb_all (component, glyphs); }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::intersects(hb_set_t const*) const
Line
Count
Source
30
67.2k
  { return hb_all (component, glyphs); }
31
32
  bool intersects_lig_glyph (const hb_set_t *glyphs) const
33
15.2k
  { return glyphs->has(ligGlyph); }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::intersects_lig_glyph(hb_set_t const*) const
Line
Count
Source
33
8.99k
  { return glyphs->has(ligGlyph); }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::intersects_lig_glyph(hb_set_t const*) const
Line
Count
Source
33
6.29k
  { return glyphs->has(ligGlyph); }
34
35
  void closure (hb_closure_context_t *c) const
36
1.10M
  {
37
1.10M
    if (!intersects (c->glyphs)) return;
38
1.06M
    c->output->add (ligGlyph);
39
1.06M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::closure(OT::hb_closure_context_t*) const
Line
Count
Source
36
1.06M
  {
37
1.06M
    if (!intersects (c->glyphs)) return;
38
1.03M
    c->output->add (ligGlyph);
39
1.03M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::closure(OT::hb_closure_context_t*) const
Line
Count
Source
36
45.9k
  {
37
45.9k
    if (!intersects (c->glyphs)) return;
38
36.9k
    c->output->add (ligGlyph);
39
36.9k
  }
40
41
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
42
0
  {
43
0
    c->input->add_array (component.arrayZ, component.get_length ());
44
0
    c->output->add (ligGlyph);
45
0
  }
Unexecuted instantiation: OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::collect_glyphs(OT::hb_collect_glyphs_context_t*) const
Unexecuted instantiation: OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::collect_glyphs(OT::hb_collect_glyphs_context_t*) const
46
47
  bool would_apply (hb_would_apply_context_t *c) const
48
764k
  {
49
764k
    if (c->len != component.lenP1)
50
718k
      return false;
51
52
45.9k
    for (unsigned int i = 1; i < c->len; i++)
53
45.8k
      if (likely (c->glyphs[i] != component[i]))
54
45.5k
        return false;
55
56
129
    return true;
57
45.7k
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::would_apply(OT::hb_would_apply_context_t*) const
Line
Count
Source
48
760k
  {
49
760k
    if (c->len != component.lenP1)
50
715k
      return false;
51
52
45.3k
    for (unsigned int i = 1; i < c->len; i++)
53
45.2k
      if (likely (c->glyphs[i] != component[i]))
54
45.0k
        return false;
55
56
113
    return true;
57
45.1k
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::would_apply(OT::hb_would_apply_context_t*) const
Line
Count
Source
48
3.72k
  {
49
3.72k
    if (c->len != component.lenP1)
50
3.13k
      return false;
51
52
648
    for (unsigned int i = 1; i < c->len; i++)
53
632
      if (likely (c->glyphs[i] != component[i]))
54
575
        return false;
55
56
16
    return true;
57
591
  }
58
59
  bool apply (hb_ot_apply_context_t *c) const
60
25.1M
  {
61
25.1M
    TRACE_APPLY (this);
62
25.1M
    unsigned int count = component.lenP1;
63
64
25.1M
    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
4.68M
    if (unlikely (count == 1))
69
14.3k
    {
70
71
14.3k
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
72
0
      {
73
0
  c->buffer->sync_so_far ();
74
0
  c->buffer->message (c->font,
75
0
          "replacing glyph at %u (ligature substitution)",
76
0
          c->buffer->idx);
77
0
      }
78
79
14.3k
      c->replace_glyph (ligGlyph);
80
81
14.3k
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
82
0
      {
83
0
  c->buffer->message (c->font,
84
0
          "replaced glyph at %u (ligature substitution)",
85
0
          c->buffer->idx - 1u);
86
0
      }
87
88
14.3k
      return_trace (true);
89
14.3k
    }
90
91
4.66M
    unsigned int total_component_count = 0;
92
93
4.66M
    if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
94
4.60M
    unsigned match_positions_stack[4];
95
4.60M
    unsigned *match_positions = match_positions_stack;
96
4.60M
    if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
97
1.00M
    {
98
1.00M
      match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
99
1.00M
      if (unlikely (!match_positions))
100
3.32k
  return_trace (false);
101
1.00M
    }
102
103
4.59M
    unsigned int match_end = 0;
104
105
4.59M
    if (likely (!match_input (c, count,
106
4.59M
                              &component[1],
107
4.59M
                              match_glyph,
108
4.59M
                              nullptr,
109
4.59M
                              &match_end,
110
4.59M
                              match_positions,
111
4.59M
                              &total_component_count)))
112
4.07M
    {
113
4.07M
      c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
114
4.07M
      if (match_positions != match_positions_stack)
115
997k
        hb_free (match_positions);
116
4.07M
      return_trace (false);
117
4.07M
    }
118
119
521k
    unsigned pos = 0;
120
521k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
121
0
    {
122
0
      unsigned delta = c->buffer->sync_so_far ();
123
124
0
      pos = c->buffer->idx;
125
126
0
      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
127
0
      char *p = buf;
128
129
0
      match_end += delta;
130
0
      for (unsigned i = 0; i < count; i++)
131
0
      {
132
0
  match_positions[i] += delta;
133
0
  if (i)
134
0
    *p++ = ',';
135
0
  snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
136
0
  p += strlen(p);
137
0
      }
138
139
0
      c->buffer->message (c->font,
140
0
        "ligating glyphs at %s",
141
0
        buf);
142
0
    }
143
144
521k
    ligate_input (c,
145
521k
                  count,
146
521k
                  match_positions,
147
521k
                  match_end,
148
521k
                  ligGlyph,
149
521k
                  total_component_count);
150
151
521k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
152
0
    {
153
0
      c->buffer->sync_so_far ();
154
0
      c->buffer->message (c->font,
155
0
        "ligated glyph at %u",
156
0
        pos);
157
0
    }
158
159
521k
    if (match_positions != match_positions_stack)
160
8.61k
      hb_free (match_positions);
161
521k
    return_trace (true);
162
4.59M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::apply(OT::hb_ot_apply_context_t*) const
Line
Count
Source
60
24.0M
  {
61
24.0M
    TRACE_APPLY (this);
62
24.0M
    unsigned int count = component.lenP1;
63
64
24.0M
    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
4.61M
    if (unlikely (count == 1))
69
13.4k
    {
70
71
13.4k
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
72
0
      {
73
0
  c->buffer->sync_so_far ();
74
0
  c->buffer->message (c->font,
75
0
          "replacing glyph at %u (ligature substitution)",
76
0
          c->buffer->idx);
77
0
      }
78
79
13.4k
      c->replace_glyph (ligGlyph);
80
81
13.4k
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
82
0
      {
83
0
  c->buffer->message (c->font,
84
0
          "replaced glyph at %u (ligature substitution)",
85
0
          c->buffer->idx - 1u);
86
0
      }
87
88
13.4k
      return_trace (true);
89
13.4k
    }
90
91
4.59M
    unsigned int total_component_count = 0;
92
93
4.59M
    if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
94
4.53M
    unsigned match_positions_stack[4];
95
4.53M
    unsigned *match_positions = match_positions_stack;
96
4.53M
    if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
97
994k
    {
98
994k
      match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
99
994k
      if (unlikely (!match_positions))
100
3.08k
  return_trace (false);
101
994k
    }
102
103
4.53M
    unsigned int match_end = 0;
104
105
4.53M
    if (likely (!match_input (c, count,
106
4.53M
                              &component[1],
107
4.53M
                              match_glyph,
108
4.53M
                              nullptr,
109
4.53M
                              &match_end,
110
4.53M
                              match_positions,
111
4.53M
                              &total_component_count)))
112
4.01M
    {
113
4.01M
      c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
114
4.01M
      if (match_positions != match_positions_stack)
115
982k
        hb_free (match_positions);
116
4.01M
      return_trace (false);
117
4.01M
    }
118
119
519k
    unsigned pos = 0;
120
519k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
121
0
    {
122
0
      unsigned delta = c->buffer->sync_so_far ();
123
124
0
      pos = c->buffer->idx;
125
126
0
      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
127
0
      char *p = buf;
128
129
0
      match_end += delta;
130
0
      for (unsigned i = 0; i < count; i++)
131
0
      {
132
0
  match_positions[i] += delta;
133
0
  if (i)
134
0
    *p++ = ',';
135
0
  snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
136
0
  p += strlen(p);
137
0
      }
138
139
0
      c->buffer->message (c->font,
140
0
        "ligating glyphs at %s",
141
0
        buf);
142
0
    }
143
144
519k
    ligate_input (c,
145
519k
                  count,
146
519k
                  match_positions,
147
519k
                  match_end,
148
519k
                  ligGlyph,
149
519k
                  total_component_count);
150
151
519k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
152
0
    {
153
0
      c->buffer->sync_so_far ();
154
0
      c->buffer->message (c->font,
155
0
        "ligated glyph at %u",
156
0
        pos);
157
0
    }
158
159
519k
    if (match_positions != match_positions_stack)
160
8.29k
      hb_free (match_positions);
161
519k
    return_trace (true);
162
4.53M
  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::apply(OT::hb_ot_apply_context_t*) const
Line
Count
Source
60
1.09M
  {
61
1.09M
    TRACE_APPLY (this);
62
1.09M
    unsigned int count = component.lenP1;
63
64
1.09M
    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
68.6k
    if (unlikely (count == 1))
69
902
    {
70
71
902
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
72
0
      {
73
0
  c->buffer->sync_so_far ();
74
0
  c->buffer->message (c->font,
75
0
          "replacing glyph at %u (ligature substitution)",
76
0
          c->buffer->idx);
77
0
      }
78
79
902
      c->replace_glyph (ligGlyph);
80
81
902
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
82
0
      {
83
0
  c->buffer->message (c->font,
84
0
          "replaced glyph at %u (ligature substitution)",
85
0
          c->buffer->idx - 1u);
86
0
      }
87
88
902
      return_trace (true);
89
902
    }
90
91
67.7k
    unsigned int total_component_count = 0;
92
93
67.7k
    if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
94
67.0k
    unsigned match_positions_stack[4];
95
67.0k
    unsigned *match_positions = match_positions_stack;
96
67.0k
    if (unlikely (count > ARRAY_LENGTH (match_positions_stack)))
97
15.6k
    {
98
15.6k
      match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned));
99
15.6k
      if (unlikely (!match_positions))
100
237
  return_trace (false);
101
15.6k
    }
102
103
66.8k
    unsigned int match_end = 0;
104
105
66.8k
    if (likely (!match_input (c, count,
106
66.8k
                              &component[1],
107
66.8k
                              match_glyph,
108
66.8k
                              nullptr,
109
66.8k
                              &match_end,
110
66.8k
                              match_positions,
111
66.8k
                              &total_component_count)))
112
64.8k
    {
113
64.8k
      c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
114
64.8k
      if (match_positions != match_positions_stack)
115
15.0k
        hb_free (match_positions);
116
64.8k
      return_trace (false);
117
64.8k
    }
118
119
1.91k
    unsigned pos = 0;
120
1.91k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
121
0
    {
122
0
      unsigned delta = c->buffer->sync_so_far ();
123
124
0
      pos = c->buffer->idx;
125
126
0
      char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
127
0
      char *p = buf;
128
129
0
      match_end += delta;
130
0
      for (unsigned i = 0; i < count; i++)
131
0
      {
132
0
  match_positions[i] += delta;
133
0
  if (i)
134
0
    *p++ = ',';
135
0
  snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
136
0
  p += strlen(p);
137
0
      }
138
139
0
      c->buffer->message (c->font,
140
0
        "ligating glyphs at %s",
141
0
        buf);
142
0
    }
143
144
1.91k
    ligate_input (c,
145
1.91k
                  count,
146
1.91k
                  match_positions,
147
1.91k
                  match_end,
148
1.91k
                  ligGlyph,
149
1.91k
                  total_component_count);
150
151
1.91k
    if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
152
0
    {
153
0
      c->buffer->sync_so_far ();
154
0
      c->buffer->message (c->font,
155
0
        "ligated glyph at %u",
156
0
        pos);
157
0
    }
158
159
1.91k
    if (match_positions != match_positions_stack)
160
316
      hb_free (match_positions);
161
1.91k
    return_trace (true);
162
66.8k
  }
163
164
  template <typename Iterator,
165
            hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
166
  bool serialize (hb_serialize_context_t *c,
167
                  hb_codepoint_t ligature,
168
                  Iterator components /* Starting from second */)
169
254k
  {
170
254k
    TRACE_SERIALIZE (this);
171
254k
    if (unlikely (!c->extend_min (this))) return_trace (false);
172
254k
    ligGlyph = ligature;
173
254k
    if (unlikely (!component.serialize (c, components))) return_trace (false);
174
254k
    return_trace (true);
175
254k
  }
_ZN2OT6Layout9GSUB_impl8LigatureINS0_10SmallTypesEE9serializeI10hb_array_tIKNS_11HBGlyphID16EETnPN12hb_enable_ifIXsr15hb_is_source_ofIT_jEE5valueEvE4typeELPv0EEEbP22hb_serialize_context_tjSB_
Line
Count
Source
169
13.1k
  {
170
13.1k
    TRACE_SERIALIZE (this);
171
13.1k
    if (unlikely (!c->extend_min (this))) return_trace (false);
172
13.0k
    ligGlyph = ligature;
173
13.0k
    if (unlikely (!component.serialize (c, components))) return_trace (false);
174
13.0k
    return_trace (true);
175
13.0k
  }
_ZN2OT6Layout9GSUB_impl8LigatureINS0_10SmallTypesEE9serializeI13hb_map_iter_tI10hb_array_tIKNS_11HBGlyphID16EERK8hb_map_tL24hb_function_sortedness_t0ELPv0EETnPN12hb_enable_ifIXsr15hb_is_source_ofIT_jEE5valueEvE4typeELSF_0EEEbP22hb_serialize_context_tjSI_
Line
Count
Source
169
238k
  {
170
238k
    TRACE_SERIALIZE (this);
171
238k
    if (unlikely (!c->extend_min (this))) return_trace (false);
172
238k
    ligGlyph = ligature;
173
238k
    if (unlikely (!component.serialize (c, components))) return_trace (false);
174
237k
    return_trace (true);
175
238k
  }
_ZN2OT6Layout9GSUB_impl8LigatureINS0_11MediumTypesEE9serializeI13hb_map_iter_tI10hb_array_tIKNS_11HBGlyphID24EERK8hb_map_tL24hb_function_sortedness_t0ELPv0EETnPN12hb_enable_ifIXsr15hb_is_source_ofIT_jEE5valueEvE4typeELSF_0EEEbP22hb_serialize_context_tjSI_
Line
Count
Source
169
3.66k
  {
170
3.66k
    TRACE_SERIALIZE (this);
171
3.66k
    if (unlikely (!c->extend_min (this))) return_trace (false);
172
3.65k
    ligGlyph = ligature;
173
3.65k
    if (unlikely (!component.serialize (c, components))) return_trace (false);
174
3.65k
    return_trace (true);
175
3.65k
  }
176
177
  bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
178
263k
  {
179
263k
    TRACE_SUBSET (this);
180
263k
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
181
263k
    const hb_map_t &glyph_map = *c->plan->glyph_map;
182
183
263k
    if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
184
    // Ensure Coverage table is always packed after this.
185
241k
    c->serializer->add_virtual_link (coverage_idx);
186
187
241k
    auto it =
188
241k
      + hb_iter (component)
189
241k
      | hb_map (glyph_map)
190
241k
      ;
191
192
241k
    auto *out = c->serializer->start_embed (*this);
193
241k
    return_trace (out->serialize (c->serializer,
194
263k
                                  glyph_map[ligGlyph],
195
263k
                                  it));  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::SmallTypes>::subset(hb_subset_context_t*, unsigned int) const
Line
Count
Source
178
252k
  {
179
252k
    TRACE_SUBSET (this);
180
252k
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
181
252k
    const hb_map_t &glyph_map = *c->plan->glyph_map;
182
183
252k
    if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
184
    // Ensure Coverage table is always packed after this.
185
238k
    c->serializer->add_virtual_link (coverage_idx);
186
187
238k
    auto it =
188
238k
      + hb_iter (component)
189
238k
      | hb_map (glyph_map)
190
238k
      ;
191
192
238k
    auto *out = c->serializer->start_embed (*this);
193
238k
    return_trace (out->serialize (c->serializer,
194
252k
                                  glyph_map[ligGlyph],
195
252k
                                  it));  }
OT::Layout::GSUB_impl::Ligature<OT::Layout::MediumTypes>::subset(hb_subset_context_t*, unsigned int) const
Line
Count
Source
178
11.8k
  {
179
11.8k
    TRACE_SUBSET (this);
180
11.8k
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
181
11.8k
    const hb_map_t &glyph_map = *c->plan->glyph_map;
182
183
11.8k
    if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
184
    // Ensure Coverage table is always packed after this.
185
3.66k
    c->serializer->add_virtual_link (coverage_idx);
186
187
3.66k
    auto it =
188
3.66k
      + hb_iter (component)
189
3.66k
      | hb_map (glyph_map)
190
3.66k
      ;
191
192
3.66k
    auto *out = c->serializer->start_embed (*this);
193
3.66k
    return_trace (out->serialize (c->serializer,
194
11.8k
                                  glyph_map[ligGlyph],
195
11.8k
                                  it));  }
196
};
197
198
199
}
200
}
201
}
202
203
#endif  /* OT_LAYOUT_GSUB_LIGATURE_HH */