Mercurial > hgrepos > Python2 > PyMuPDF
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 */ |
