gsubgpos-graph.hh (14672B)
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 "../OT/Layout/GSUB/SubstLookupSubTable.hh" 31 #include "gsubgpos-context.hh" 32 #include "pairpos-graph.hh" 33 #include "markbasepos-graph.hh" 34 #include "ligature-graph.hh" 35 36 #ifndef GRAPH_GSUBGPOS_GRAPH_HH 37 #define GRAPH_GSUBGPOS_GRAPH_HH 38 39 namespace graph { 40 41 struct Lookup; 42 43 template<typename T> 44 struct ExtensionFormat1 : public OT::ExtensionFormat1<T> 45 { 46 void reset(unsigned type) 47 { 48 this->format = 1; 49 this->extensionLookupType = type; 50 this->extensionOffset = 0; 51 } 52 53 bool sanitize (graph_t::vertex_t& vertex) const 54 { 55 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 56 return vertex_len >= OT::ExtensionFormat1<T>::static_size; 57 } 58 59 unsigned get_lookup_type () const 60 { 61 return this->extensionLookupType; 62 } 63 64 unsigned get_subtable_index (graph_t& graph, unsigned this_index) const 65 { 66 return graph.index_for_offset (this_index, &this->extensionOffset); 67 } 68 }; 69 70 struct Lookup : public OT::Lookup 71 { 72 unsigned number_of_subtables () const 73 { 74 return subTable.len; 75 } 76 77 bool sanitize (graph_t::vertex_t& vertex) const 78 { 79 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 80 if (vertex_len < OT::Lookup::min_size) return false; 81 hb_barrier (); 82 return vertex_len >= this->get_size (); 83 } 84 85 bool is_extension (hb_tag_t table_tag) const 86 { 87 return lookupType == extension_type (table_tag); 88 } 89 90 bool use_mark_filtering_set () const 91 { 92 unsigned flag = lookupFlag; 93 return flag & 0x0010u; 94 } 95 96 bool make_extension (gsubgpos_graph_context_t& c, 97 unsigned this_index) 98 { 99 unsigned type = lookupType; 100 unsigned ext_type = extension_type (c.table_tag); 101 if (!ext_type || is_extension (c.table_tag)) 102 { 103 // NOOP 104 return true; 105 } 106 107 DEBUG_MSG (SUBSET_REPACK, nullptr, 108 "Promoting lookup type %u (obj %u) to extension.", 109 type, 110 this_index); 111 112 for (unsigned i = 0; i < subTable.len; i++) 113 { 114 unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); 115 if (!make_subtable_extension (c, 116 this_index, 117 subtable_index)) 118 return false; 119 } 120 121 lookupType = ext_type; 122 return true; 123 } 124 125 bool split_subtables_if_needed (gsubgpos_graph_context_t& c, 126 unsigned this_index) 127 { 128 unsigned type = lookupType; 129 bool is_ext = is_extension (c.table_tag); 130 131 if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB) 132 return true; 133 134 if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) 135 return true; 136 137 hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables; 138 for (unsigned i = 0; i < subTable.len; i++) 139 { 140 unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); 141 if (is_ext) { 142 unsigned ext_subtable_index = subtable_index; 143 ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension = 144 (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) 145 c.graph.object (ext_subtable_index).head; 146 if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index])) 147 continue; 148 149 subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); 150 type = extension->get_lookup_type (); 151 if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) 152 continue; 153 } 154 155 hb_vector_t<unsigned>* split_result; 156 if (c.split_subtables.has (subtable_index, &split_result)) 157 { 158 if (split_result->length == 0) 159 continue; 160 all_new_subtables.push (hb_pair(i, *split_result)); 161 } 162 else 163 { 164 hb_vector_t<unsigned> new_sub_tables; 165 166 if (c.table_tag == HB_OT_TAG_GPOS) { 167 switch (type) 168 { 169 case 2: 170 new_sub_tables = split_subtable<PairPos> (c, subtable_index); break; 171 case 4: 172 new_sub_tables = split_subtable<MarkBasePos> (c, subtable_index); break; 173 default: 174 break; 175 } 176 } else if (c.table_tag == HB_OT_TAG_GSUB) { 177 switch (type) 178 { 179 case 4: 180 new_sub_tables = split_subtable<graph::LigatureSubst> (c, subtable_index); break; 181 default: 182 break; 183 } 184 } 185 186 if (new_sub_tables.in_error ()) return false; 187 188 c.split_subtables.set (subtable_index, new_sub_tables); 189 if (new_sub_tables) 190 all_new_subtables.push (hb_pair (i, std::move (new_sub_tables))); 191 } 192 } 193 194 if (all_new_subtables) { 195 return add_sub_tables (c, this_index, type, all_new_subtables); 196 } 197 198 return true; 199 } 200 201 template<typename T> 202 hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c, 203 unsigned objidx) 204 { 205 T* sub_table = (T*) c.graph.object (objidx).head; 206 if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) 207 return hb_vector_t<unsigned> (); 208 209 return sub_table->split_subtables (c, objidx); 210 } 211 212 bool add_sub_tables (gsubgpos_graph_context_t& c, 213 unsigned this_index, 214 unsigned type, 215 const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) 216 { 217 bool is_ext = is_extension (c.table_tag); 218 auto* v = &c.graph.vertices_[this_index]; 219 fix_existing_subtable_links (c, this_index, subtable_ids); 220 221 unsigned new_subtable_count = 0; 222 for (const auto& p : subtable_ids) 223 new_subtable_count += p.second.length; 224 225 size_t new_size = v->table_size () 226 + new_subtable_count * OT::Offset16::static_size; 227 char* buffer = (char*) hb_calloc (1, new_size); 228 if (!buffer) return false; 229 if (!c.add_buffer (buffer)) 230 { 231 hb_free (buffer); 232 return false; 233 } 234 hb_memcpy (buffer, v->obj.head, v->table_size()); 235 236 if (use_mark_filtering_set ()) 237 hb_memcpy (buffer + new_size - 2, v->obj.tail - 2, 2); 238 239 v->obj.head = buffer; 240 v->obj.tail = buffer + new_size; 241 242 Lookup* new_lookup = (Lookup*) buffer; 243 244 unsigned shift = 0; 245 new_lookup->subTable.len = subTable.len + new_subtable_count; 246 for (const auto& p : subtable_ids) 247 { 248 unsigned offset_index = p.first + shift + 1; 249 shift += p.second.length; 250 251 for (unsigned subtable_id : p.second) 252 { 253 if (is_ext) 254 { 255 unsigned ext_id = create_extension_subtable (c, subtable_id, type); 256 c.graph.vertices_[subtable_id].add_parent (ext_id, false); 257 subtable_id = ext_id; 258 // the reference to v may have changed on adding a node, so reassign it. 259 v = &c.graph.vertices_[this_index]; 260 } 261 262 auto* link = v->obj.real_links.push (); 263 link->width = 2; 264 link->objidx = subtable_id; 265 link->position = (char*) &new_lookup->subTable[offset_index++] - 266 (char*) new_lookup; 267 c.graph.vertices_[subtable_id].add_parent (this_index, false); 268 } 269 } 270 271 // Repacker sort order depends on link order, which we've messed up so resort it. 272 v->obj.real_links.qsort (); 273 274 // The head location of the lookup has changed, invalidating the lookups map entry 275 // in the context. Update the map. 276 c.lookups.set (this_index, new_lookup); 277 return true; 278 } 279 280 void fix_existing_subtable_links (gsubgpos_graph_context_t& c, 281 unsigned this_index, 282 const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) 283 { 284 auto& v = c.graph.vertices_[this_index]; 285 Lookup* lookup = (Lookup*) v.obj.head; 286 287 unsigned shift = 0; 288 for (const auto& p : subtable_ids) 289 { 290 unsigned insert_index = p.first + shift; 291 unsigned pos_offset = p.second.length * OT::Offset16::static_size; 292 unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; 293 shift += p.second.length; 294 295 for (auto& l : v.obj.all_links_writer ()) 296 { 297 if (l.position > insert_offset) l.position += pos_offset; 298 } 299 } 300 } 301 302 unsigned create_extension_subtable (gsubgpos_graph_context_t& c, 303 unsigned subtable_index, 304 unsigned type) 305 { 306 unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size; 307 308 unsigned ext_index = c.create_node (extension_size); 309 if (ext_index == (unsigned) -1) 310 return -1; 311 312 auto& ext_vertex = c.graph.vertices_[ext_index]; 313 ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension = 314 (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) ext_vertex.obj.head; 315 extension->reset (type); 316 317 // Make extension point at the subtable. 318 auto* l = ext_vertex.obj.real_links.push (); 319 320 l->width = 4; 321 l->objidx = subtable_index; 322 l->position = 4; 323 324 return ext_index; 325 } 326 327 bool make_subtable_extension (gsubgpos_graph_context_t& c, 328 unsigned lookup_index, 329 unsigned subtable_index) 330 { 331 unsigned type = lookupType; 332 unsigned ext_index = -1; 333 unsigned* existing_ext_index = nullptr; 334 if (c.subtable_to_extension.has(subtable_index, &existing_ext_index)) { 335 ext_index = *existing_ext_index; 336 } else { 337 ext_index = create_extension_subtable(c, subtable_index, type); 338 c.subtable_to_extension.set(subtable_index, ext_index); 339 } 340 341 if (ext_index == (unsigned) -1) 342 return false; 343 344 auto& subtable_vertex = c.graph.vertices_[subtable_index]; 345 auto& lookup_vertex = c.graph.vertices_[lookup_index]; 346 for (auto& l : lookup_vertex.obj.real_links.writer ()) 347 { 348 if (l.objidx == subtable_index) { 349 // Change lookup to point at the extension. 350 l.objidx = ext_index; 351 if (existing_ext_index) 352 subtable_vertex.remove_parent(lookup_index); 353 } 354 } 355 356 // Make extension point at the subtable. 357 auto& ext_vertex = c.graph.vertices_[ext_index]; 358 ext_vertex.add_parent (lookup_index, false); 359 if (!existing_ext_index) 360 subtable_vertex.remap_parent (lookup_index, ext_index); 361 362 return true; 363 } 364 365 private: 366 bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const { 367 return (c.table_tag == HB_OT_TAG_GSUB) && ( 368 type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature 369 ); 370 } 371 372 bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const { 373 return (c.table_tag == HB_OT_TAG_GPOS) && ( 374 type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair || 375 type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase 376 ); 377 } 378 379 unsigned extension_type (hb_tag_t table_tag) const 380 { 381 switch (table_tag) 382 { 383 case HB_OT_TAG_GPOS: return 9; 384 case HB_OT_TAG_GSUB: return 7; 385 default: return 0; 386 } 387 } 388 }; 389 390 template <typename T> 391 struct LookupList : public OT::LookupList<T> 392 { 393 bool sanitize (const graph_t::vertex_t& vertex) const 394 { 395 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 396 if (vertex_len < OT::LookupList<T>::min_size) return false; 397 hb_barrier (); 398 return vertex_len >= OT::LookupList<T>::item_size * this->len; 399 } 400 }; 401 402 struct GSTAR : public OT::GSUBGPOS 403 { 404 static GSTAR* graph_to_gstar (graph_t& graph) 405 { 406 const auto& r = graph.root (); 407 408 GSTAR* gstar = (GSTAR*) r.obj.head; 409 if (!gstar || !gstar->sanitize (r)) 410 return nullptr; 411 hb_barrier (); 412 413 return gstar; 414 } 415 416 const void* get_lookup_list_field_offset () const 417 { 418 switch (u.version.major) { 419 case 1: return u.version1.get_lookup_list_offset (); 420 #ifndef HB_NO_BEYOND_64K 421 case 2: return u.version2.get_lookup_list_offset (); 422 #endif 423 default: return 0; 424 } 425 } 426 427 bool sanitize (const graph_t::vertex_t& vertex) 428 { 429 int64_t len = vertex.obj.tail - vertex.obj.head; 430 if (len < OT::GSUBGPOS::min_size) return false; 431 hb_barrier (); 432 return len >= get_size (); 433 } 434 435 void find_lookups (graph_t& graph, 436 hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */) 437 { 438 switch (u.version.major) { 439 case 1: find_lookups<SmallTypes> (graph, lookups); break; 440 #ifndef HB_NO_BEYOND_64K 441 case 2: find_lookups<MediumTypes> (graph, lookups); break; 442 #endif 443 } 444 } 445 446 unsigned get_lookup_list_index (graph_t& graph) 447 { 448 return graph.index_for_offset (graph.root_idx (), 449 get_lookup_list_field_offset()); 450 } 451 452 template<typename Types> 453 void find_lookups (graph_t& graph, 454 hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */) 455 { 456 unsigned lookup_list_idx = get_lookup_list_index (graph); 457 const LookupList<Types>* lookupList = 458 (const LookupList<Types>*) graph.object (lookup_list_idx).head; 459 if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx])) 460 return; 461 462 for (unsigned i = 0; i < lookupList->len; i++) 463 { 464 unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); 465 Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; 466 if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue; 467 lookups.set (lookup_idx, lookup); 468 } 469 } 470 }; 471 472 473 474 475 } 476 477 #endif /* GRAPH_GSUBGPOS_GRAPH_HH */