• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2022  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Garret Rieger
25  */
26 
27 #include "graph.hh"
28 #include "../hb-ot-layout-gsubgpos.hh"
29 #include "../OT/Layout/GSUB/ExtensionSubst.hh"
30 #include "gsubgpos-context.hh"
31 #include "pairpos-graph.hh"
32 #include "markbasepos-graph.hh"
33 
34 #ifndef GRAPH_GSUBGPOS_GRAPH_HH
35 #define GRAPH_GSUBGPOS_GRAPH_HH
36 
37 namespace graph {
38 
39 struct Lookup;
40 
41 template<typename T>
42 struct ExtensionFormat1 : public OT::ExtensionFormat1<T>
43 {
resetgraph::ExtensionFormat144   void reset(unsigned type)
45   {
46     this->format = 1;
47     this->extensionLookupType = type;
48     this->extensionOffset = 0;
49   }
50 
sanitizegraph::ExtensionFormat151   bool sanitize (graph_t::vertex_t& vertex) const
52   {
53     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
54     return vertex_len >= OT::ExtensionFormat1<T>::static_size;
55   }
56 
get_lookup_typegraph::ExtensionFormat157   unsigned get_lookup_type () const
58   {
59     return this->extensionLookupType;
60   }
61 
get_subtable_indexgraph::ExtensionFormat162   unsigned get_subtable_index (graph_t& graph, unsigned this_index) const
63   {
64     return graph.index_for_offset (this_index, &this->extensionOffset);
65   }
66 };
67 
68 struct Lookup : public OT::Lookup
69 {
number_of_subtablesgraph::Lookup70   unsigned number_of_subtables () const
71   {
72     return subTable.len;
73   }
74 
sanitizegraph::Lookup75   bool sanitize (graph_t::vertex_t& vertex) const
76   {
77     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
78     if (vertex_len < OT::Lookup::min_size) return false;
79     return vertex_len >= this->get_size ();
80   }
81 
is_extensiongraph::Lookup82   bool is_extension (hb_tag_t table_tag) const
83   {
84     return lookupType == extension_type (table_tag);
85   }
86 
make_extensiongraph::Lookup87   bool make_extension (gsubgpos_graph_context_t& c,
88                        unsigned this_index)
89   {
90     unsigned type = lookupType;
91     unsigned ext_type = extension_type (c.table_tag);
92     if (!ext_type || is_extension (c.table_tag))
93     {
94       // NOOP
95       return true;
96     }
97 
98     DEBUG_MSG (SUBSET_REPACK, nullptr,
99                "Promoting lookup type %u (obj %u) to extension.",
100                type,
101                this_index);
102 
103     for (unsigned i = 0; i < subTable.len; i++)
104     {
105       unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
106       if (!make_subtable_extension (c,
107                                     this_index,
108                                     subtable_index))
109         return false;
110     }
111 
112     lookupType = ext_type;
113     return true;
114   }
115 
split_subtables_if_neededgraph::Lookup116   bool split_subtables_if_needed (gsubgpos_graph_context_t& c,
117                                   unsigned this_index)
118   {
119     unsigned type = lookupType;
120     bool is_ext = is_extension (c.table_tag);
121 
122     if (c.table_tag != HB_OT_TAG_GPOS)
123       return true;
124 
125     if (!is_ext &&
126         type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair &&
127         type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
128       return true;
129 
130     hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables;
131     for (unsigned i = 0; i < subTable.len; i++)
132     {
133       unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
134       unsigned parent_index = this_index;
135       if (is_ext) {
136         unsigned ext_subtable_index = subtable_index;
137         parent_index = ext_subtable_index;
138         ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
139             (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*)
140             c.graph.object (ext_subtable_index).head;
141         if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index]))
142           continue;
143 
144         subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
145         type = extension->get_lookup_type ();
146         if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair
147             && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
148           continue;
149       }
150 
151       hb_vector_t<unsigned> new_sub_tables;
152       switch (type)
153       {
154       case 2:
155         new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
156       case 4:
157         new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
158       default:
159         break;
160       }
161       if (new_sub_tables.in_error ()) return false;
162       if (!new_sub_tables) continue;
163       hb_pair_t<unsigned, hb_vector_t<unsigned>>* entry = all_new_subtables.push ();
164       entry->first = i;
165       entry->second = std::move (new_sub_tables);
166     }
167 
168     if (all_new_subtables) {
169       add_sub_tables (c, this_index, type, all_new_subtables);
170     }
171 
172     return true;
173   }
174 
175   template<typename T>
split_subtablegraph::Lookup176   hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c,
177                                         unsigned parent_idx,
178                                         unsigned objidx)
179   {
180     T* sub_table = (T*) c.graph.object (objidx).head;
181     if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
182       return hb_vector_t<unsigned> ();
183 
184     return sub_table->split_subtables (c, parent_idx, objidx);
185   }
186 
add_sub_tablesgraph::Lookup187   void add_sub_tables (gsubgpos_graph_context_t& c,
188                        unsigned this_index,
189                        unsigned type,
190                        hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
191   {
192     bool is_ext = is_extension (c.table_tag);
193     auto& v = c.graph.vertices_[this_index];
194     fix_existing_subtable_links (c, this_index, subtable_ids);
195 
196     unsigned new_subtable_count = 0;
197     for (const auto& p : subtable_ids)
198       new_subtable_count += p.second.length;
199 
200     size_t new_size = v.table_size ()
201                       + new_subtable_count * OT::Offset16::static_size;
202     char* buffer = (char*) hb_calloc (1, new_size);
203     c.add_buffer (buffer);
204     hb_memcpy (buffer, v.obj.head, v.table_size());
205 
206     v.obj.head = buffer;
207     v.obj.tail = buffer + new_size;
208 
209     Lookup* new_lookup = (Lookup*) buffer;
210 
211     unsigned shift = 0;
212     new_lookup->subTable.len = subTable.len + new_subtable_count;
213     for (const auto& p : subtable_ids)
214     {
215       unsigned offset_index = p.first + shift + 1;
216       shift += p.second.length;
217 
218       for (unsigned subtable_id : p.second)
219       {
220         if (is_ext)
221         {
222           unsigned ext_id = create_extension_subtable (c, subtable_id, type);
223           c.graph.vertices_[subtable_id].parents.push (ext_id);
224           subtable_id = ext_id;
225         }
226 
227         auto* link = v.obj.real_links.push ();
228         link->width = 2;
229         link->objidx = subtable_id;
230         link->position = (char*) &new_lookup->subTable[offset_index++] -
231                          (char*) new_lookup;
232         c.graph.vertices_[subtable_id].parents.push (this_index);
233       }
234     }
235 
236     // Repacker sort order depends on link order, which we've messed up so resort it.
237     v.obj.real_links.qsort ();
238 
239     // The head location of the lookup has changed, invalidating the lookups map entry
240     // in the context. Update the map.
241     c.lookups.set (this_index, new_lookup);
242   }
243 
fix_existing_subtable_linksgraph::Lookup244   void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
245                                     unsigned this_index,
246                                     hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
247   {
248     auto& v = c.graph.vertices_[this_index];
249     Lookup* lookup = (Lookup*) v.obj.head;
250 
251     unsigned shift = 0;
252     for (const auto& p : subtable_ids)
253     {
254       unsigned insert_index = p.first + shift;
255       unsigned pos_offset = p.second.length * OT::Offset16::static_size;
256       unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup;
257       shift += p.second.length;
258 
259       for (auto& l : v.obj.all_links_writer ())
260       {
261         if (l.position > insert_offset) l.position += pos_offset;
262       }
263     }
264   }
265 
create_extension_subtablegraph::Lookup266   unsigned create_extension_subtable (gsubgpos_graph_context_t& c,
267                                       unsigned subtable_index,
268                                       unsigned type)
269   {
270     unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
271 
272     unsigned ext_index = c.create_node (extension_size);
273     if (ext_index == (unsigned) -1)
274       return -1;
275 
276     auto& ext_vertex = c.graph.vertices_[ext_index];
277     ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
278         (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) ext_vertex.obj.head;
279     extension->reset (type);
280 
281     // Make extension point at the subtable.
282     auto* l = ext_vertex.obj.real_links.push ();
283 
284     l->width = 4;
285     l->objidx = subtable_index;
286     l->position = 4;
287 
288     return ext_index;
289   }
290 
make_subtable_extensiongraph::Lookup291   bool make_subtable_extension (gsubgpos_graph_context_t& c,
292                                 unsigned lookup_index,
293                                 unsigned subtable_index)
294   {
295     unsigned type = lookupType;
296 
297     unsigned ext_index = create_extension_subtable(c, subtable_index, type);
298     if (ext_index == (unsigned) -1)
299       return false;
300 
301     auto& lookup_vertex = c.graph.vertices_[lookup_index];
302     for (auto& l : lookup_vertex.obj.real_links.writer ())
303     {
304       if (l.objidx == subtable_index)
305         // Change lookup to point at the extension.
306         l.objidx = ext_index;
307     }
308 
309     // Make extension point at the subtable.
310     auto& ext_vertex = c.graph.vertices_[ext_index];
311     auto& subtable_vertex = c.graph.vertices_[subtable_index];
312     ext_vertex.parents.push (lookup_index);
313     subtable_vertex.remap_parent (lookup_index, ext_index);
314 
315     return true;
316   }
317 
318  private:
extension_typegraph::Lookup319   unsigned extension_type (hb_tag_t table_tag) const
320   {
321     switch (table_tag)
322     {
323     case HB_OT_TAG_GPOS: return 9;
324     case HB_OT_TAG_GSUB: return 7;
325     default: return 0;
326     }
327   }
328 };
329 
330 template <typename T>
331 struct LookupList : public OT::LookupList<T>
332 {
sanitizegraph::LookupList333   bool sanitize (const graph_t::vertex_t& vertex) const
334   {
335     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
336     if (vertex_len < OT::LookupList<T>::min_size) return false;
337     return vertex_len >= OT::LookupList<T>::item_size * this->len;
338   }
339 };
340 
341 struct GSTAR : public OT::GSUBGPOS
342 {
graph_to_gstargraph::GSTAR343   static GSTAR* graph_to_gstar (graph_t& graph)
344   {
345     const auto& r = graph.root ();
346 
347     GSTAR* gstar = (GSTAR*) r.obj.head;
348     if (!gstar || !gstar->sanitize (r))
349       return nullptr;
350 
351     return gstar;
352   }
353 
get_lookup_list_field_offsetgraph::GSTAR354   const void* get_lookup_list_field_offset () const
355   {
356     switch (u.version.major) {
357     case 1: return u.version1.get_lookup_list_offset ();
358 #ifndef HB_NO_BEYOND_64K
359     case 2: return u.version2.get_lookup_list_offset ();
360 #endif
361     default: return 0;
362     }
363   }
364 
sanitizegraph::GSTAR365   bool sanitize (const graph_t::vertex_t& vertex)
366   {
367     int64_t len = vertex.obj.tail - vertex.obj.head;
368     if (len < OT::GSUBGPOS::min_size) return false;
369     return len >= get_size ();
370   }
371 
find_lookupsgraph::GSTAR372   void find_lookups (graph_t& graph,
373                      hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
374   {
375     switch (u.version.major) {
376       case 1: find_lookups<SmallTypes> (graph, lookups); break;
377 #ifndef HB_NO_BEYOND_64K
378       case 2: find_lookups<MediumTypes> (graph, lookups); break;
379 #endif
380     }
381   }
382 
get_lookup_list_indexgraph::GSTAR383   unsigned get_lookup_list_index (graph_t& graph)
384   {
385     return graph.index_for_offset (graph.root_idx (),
386                                    get_lookup_list_field_offset());
387   }
388 
389   template<typename Types>
find_lookupsgraph::GSTAR390   void find_lookups (graph_t& graph,
391                      hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
392   {
393     unsigned lookup_list_idx = get_lookup_list_index (graph);
394     const LookupList<Types>* lookupList =
395         (const LookupList<Types>*) graph.object (lookup_list_idx).head;
396     if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
397       return;
398 
399     for (unsigned i = 0; i < lookupList->len; i++)
400     {
401       unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
402       Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
403       if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
404       lookups.set (lookup_idx, lookup);
405     }
406   }
407 };
408 
409 
410 
411 
412 }
413 
414 #endif  /* GRAPH_GSUBGPOS_GRAPH_HH */
415