xref: /aosp_15_r20/external/harfbuzz_ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 #ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
2 #define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
3 
4 #include "Common.hh"
5 
6 namespace OT {
7 namespace Layout {
8 namespace GSUB_impl {
9 
10 template <typename Types>
11 struct SingleSubstFormat1_3
12 {
13   protected:
14   HBUINT16      format;                 /* Format identifier--format = 1 */
15   typename Types::template OffsetTo<Coverage>
16                 coverage;               /* Offset to Coverage table--from
17                                          * beginning of Substitution table */
18   typename Types::HBUINT
19                 deltaGlyphID;           /* Add to original GlyphID to get
20                                          * substitute GlyphID, modulo 0x10000 */
21 
22   public:
23   DEFINE_SIZE_STATIC (2 + 2 * Types::size);
24 
sanitizeOT::Layout::GSUB_impl::SingleSubstFormat1_325   bool sanitize (hb_sanitize_context_t *c) const
26   {
27     TRACE_SANITIZE (this);
28     return_trace (c->check_struct (this) &&
29                   coverage.sanitize (c, this) &&
30                   /* The coverage  table may use a range to represent a set
31                    * of glyphs, which means a small number of bytes can
32                    * generate a large glyph set. Manually modify the
33                    * sanitizer max ops to take this into account.
34                    *
35                    * Note: This check *must* be right after coverage sanitize. */
36                   c->check_ops ((this + coverage).get_population () >> 1));
37   }
38 
get_maskOT::Layout::GSUB_impl::SingleSubstFormat1_339   hb_codepoint_t get_mask () const
40   { return (1 << (8 * Types::size)) - 1; }
41 
intersectsOT::Layout::GSUB_impl::SingleSubstFormat1_342   bool intersects (const hb_set_t *glyphs) const
43   { return (this+coverage).intersects (glyphs); }
44 
may_have_non_1to1OT::Layout::GSUB_impl::SingleSubstFormat1_345   bool may_have_non_1to1 () const
46   { return false; }
47 
closureOT::Layout::GSUB_impl::SingleSubstFormat1_348   void closure (hb_closure_context_t *c) const
49   {
50     hb_codepoint_t d = deltaGlyphID;
51     hb_codepoint_t mask = get_mask ();
52 
53     /* Help fuzzer avoid this function as much. */
54     unsigned pop = (this+coverage).get_population ();
55     if (pop >= mask)
56       return;
57 
58     hb_set_t intersection;
59     (this+coverage).intersect_set (c->parent_active_glyphs (), intersection);
60 
61     /* In degenerate fuzzer-found fonts, but not real fonts,
62      * this table can keep adding new glyphs in each round of closure.
63      * Refuse to close-over, if it maps glyph range to overlapping range. */
64     hb_codepoint_t min_before = intersection.get_min ();
65     hb_codepoint_t max_before = intersection.get_max ();
66     hb_codepoint_t min_after = (min_before + d) & mask;
67     hb_codepoint_t max_after = (max_before + d) & mask;
68     if (intersection.get_population () == max_before - min_before + 1 &&
69 	((min_before <= min_after && min_after <= max_before) ||
70 	 (min_before <= max_after && max_after <= max_before)))
71       return;
72 
73     + hb_iter (intersection)
74     | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
75     | hb_sink (c->output)
76     ;
77   }
78 
closure_lookupsOT::Layout::GSUB_impl::SingleSubstFormat1_379   void closure_lookups (hb_closure_lookups_context_t *c) const {}
80 
collect_glyphsOT::Layout::GSUB_impl::SingleSubstFormat1_381   void collect_glyphs (hb_collect_glyphs_context_t *c) const
82   {
83     if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
84     hb_codepoint_t d = deltaGlyphID;
85     hb_codepoint_t mask = get_mask ();
86 
87     + hb_iter (this+coverage)
88     | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
89     | hb_sink (c->output)
90     ;
91   }
92 
get_coverageOT::Layout::GSUB_impl::SingleSubstFormat1_393   const Coverage &get_coverage () const { return this+coverage; }
94 
would_applyOT::Layout::GSUB_impl::SingleSubstFormat1_395   bool would_apply (hb_would_apply_context_t *c) const
96   { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
97 
98   unsigned
get_glyph_alternatesOT::Layout::GSUB_impl::SingleSubstFormat1_399   get_glyph_alternates (hb_codepoint_t  glyph_id,
100                         unsigned        start_offset,
101                         unsigned       *alternate_count  /* IN/OUT.  May be NULL. */,
102                         hb_codepoint_t *alternate_glyphs /* OUT.     May be NULL. */) const
103   {
104     unsigned int index = (this+coverage).get_coverage (glyph_id);
105     if (likely (index == NOT_COVERED))
106     {
107       if (alternate_count)
108         *alternate_count = 0;
109       return 0;
110     }
111 
112     if (alternate_count && *alternate_count)
113     {
114       hb_codepoint_t d = deltaGlyphID;
115       hb_codepoint_t mask = get_mask ();
116 
117       glyph_id = (glyph_id + d) & mask;
118 
119       *alternate_glyphs = glyph_id;
120       *alternate_count = 1;
121     }
122 
123     return 1;
124   }
125 
applyOT::Layout::GSUB_impl::SingleSubstFormat1_3126   bool apply (hb_ot_apply_context_t *c) const
127   {
128     TRACE_APPLY (this);
129     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
130     unsigned int index = (this+coverage).get_coverage (glyph_id);
131     if (likely (index == NOT_COVERED)) return_trace (false);
132 
133     hb_codepoint_t d = deltaGlyphID;
134     hb_codepoint_t mask = get_mask ();
135 
136     glyph_id = (glyph_id + d) & mask;
137 
138     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
139     {
140       c->buffer->sync_so_far ();
141       c->buffer->message (c->font,
142 			  "replacing glyph at %u (single substitution)",
143 			  c->buffer->idx);
144     }
145 
146     c->replace_glyph (glyph_id);
147 
148     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
149     {
150       c->buffer->message (c->font,
151 			  "replaced glyph at %u (single substitution)",
152 			  c->buffer->idx - 1u);
153     }
154 
155     return_trace (true);
156   }
157 
158   template<typename Iterator,
159            hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
serializeOT::Layout::GSUB_impl::SingleSubstFormat1_3160   bool serialize (hb_serialize_context_t *c,
161                   Iterator glyphs,
162                   unsigned delta)
163   {
164     TRACE_SERIALIZE (this);
165     if (unlikely (!c->extend_min (this))) return_trace (false);
166     if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
167     c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
168     return_trace (true);
169   }
170 
subsetOT::Layout::GSUB_impl::SingleSubstFormat1_3171   bool subset (hb_subset_context_t *c) const
172   {
173     TRACE_SUBSET (this);
174     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
175     const hb_map_t &glyph_map = *c->plan->glyph_map;
176 
177     hb_codepoint_t d = deltaGlyphID;
178     hb_codepoint_t mask = get_mask ();
179 
180     hb_set_t intersection;
181     (this+coverage).intersect_set (glyphset, intersection);
182 
183     auto it =
184     + hb_iter (intersection)
185     | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
186                                 return hb_codepoint_pair_t (g,
187                                                             (g + d) & mask); })
188     | hb_filter (glyphset, hb_second)
189     | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
190                               { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
191     ;
192 
193     bool ret = bool (it);
194     SingleSubst_serialize (c->serializer, it);
195     return_trace (ret);
196   }
197 };
198 
199 }
200 }
201 }
202 
203 
204 #endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */
205