comparison mupdf-source/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
44 void reset(unsigned type)
45 {
46 this->format = 1;
47 this->extensionLookupType = type;
48 this->extensionOffset = 0;
49 }
50
51 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
57 unsigned get_lookup_type () const
58 {
59 return this->extensionLookupType;
60 }
61
62 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 {
70 unsigned number_of_subtables () const
71 {
72 return subTable.len;
73 }
74
75 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
82 bool is_extension (hb_tag_t table_tag) const
83 {
84 return lookupType == extension_type (table_tag);
85 }
86
87 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
116 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>
176 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
187 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
244 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
266 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
291 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:
319 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 {
333 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 {
343 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
354 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
365 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
372 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
383 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>
390 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 */