tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */