1 /*
   2  * Copyright © 2018  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, Roderick Sheeter
  25  */
  26 
  27 #include "hb-subset-plan.hh"
  28 #include "hb-map.hh"
  29 #include "hb-set.hh"
  30 
  31 #include "hb-ot-cmap-table.hh"
  32 #include "hb-ot-glyf-table.hh"
  33 #include "hb-ot-cff1-table.hh"
  34 
  35 static void
  36 _add_gid_and_children (const OT::glyf::accelerator_t &glyf,
  37                        hb_codepoint_t gid,
  38                        hb_set_t *gids_to_retain)
  39 {
  40   if (hb_set_has (gids_to_retain, gid))
  41     // Already visited this gid, ignore.
  42     return;
  43 
  44   hb_set_add (gids_to_retain, gid);
  45 
  46   OT::glyf::CompositeGlyphHeader::Iterator composite;
  47   if (glyf.get_composite (gid, &composite))
  48   {
  49     do
  50     {
  51       _add_gid_and_children (glyf, (hb_codepoint_t) composite.current->glyphIndex, gids_to_retain);
  52     } while (composite.move_to_next());
  53   }
  54 }
  55 
  56 static void
  57 _add_cff_seac_components (const OT::cff1::accelerator_t &cff,
  58            hb_codepoint_t gid,
  59            hb_set_t *gids_to_retain)
  60 {
  61   hb_codepoint_t base_gid, accent_gid;
  62   if (cff.get_seac_components (gid, &base_gid, &accent_gid))
  63   {
  64     hb_set_add (gids_to_retain, base_gid);
  65     hb_set_add (gids_to_retain, accent_gid);
  66   }
  67 }
  68 
  69 static void
  70 _gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
  71 {
  72   hb_set_t lookup_indices;
  73   hb_ot_layout_collect_lookups (face,
  74                                 HB_OT_TAG_GSUB,
  75                                 nullptr,
  76                                 nullptr,
  77                                 nullptr,
  78                                 &lookup_indices);
  79   hb_ot_layout_lookups_substitute_closure (face,
  80                                            &lookup_indices,
  81                                            gids_to_retain);
  82 }
  83 
  84 static void
  85 _remove_invalid_gids (hb_set_t *glyphs,
  86                       unsigned int num_glyphs)
  87 {
  88   hb_codepoint_t gid = HB_SET_VALUE_INVALID;
  89   while (glyphs->next (&gid))
  90   {
  91     if (gid >= num_glyphs)
  92       glyphs->del (gid);
  93   }
  94 }
  95 
  96 static hb_set_t *
  97 _populate_gids_to_retain (hb_face_t *face,
  98                           const hb_set_t *unicodes,
  99                           bool close_over_gsub,
 100                           hb_set_t *unicodes_to_retain,
 101                           hb_map_t *codepoint_to_glyph,
 102                           hb_vector_t<hb_codepoint_t> *glyphs)
 103 {
 104   OT::cmap::accelerator_t cmap;
 105   OT::glyf::accelerator_t glyf;
 106   OT::cff1::accelerator_t cff;
 107   cmap.init (face);
 108   glyf.init (face);
 109   cff.init (face);
 110 
 111   hb_set_t *initial_gids_to_retain = hb_set_create ();
 112   initial_gids_to_retain->add (0); // Not-def
 113 
 114   hb_codepoint_t cp = HB_SET_VALUE_INVALID;
 115   while (unicodes->next (&cp))
 116   {
 117     hb_codepoint_t gid;
 118     if (!cmap.get_nominal_glyph (cp, &gid))
 119     {
 120       DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
 121       continue;
 122     }
 123     unicodes_to_retain->add (cp);
 124     codepoint_to_glyph->set (cp, gid);
 125     initial_gids_to_retain->add (gid);
 126   }
 127 
 128   if (close_over_gsub)
 129     // Add all glyphs needed for GSUB substitutions.
 130     _gsub_closure (face, initial_gids_to_retain);
 131 
 132   // Populate a full set of glyphs to retain by adding all referenced
 133   // composite glyphs.
 134   hb_codepoint_t gid = HB_SET_VALUE_INVALID;
 135   hb_set_t *all_gids_to_retain = hb_set_create ();
 136   while (initial_gids_to_retain->next (&gid))
 137   {
 138     _add_gid_and_children (glyf, gid, all_gids_to_retain);
 139     if (cff.is_valid ())
 140       _add_cff_seac_components (cff, gid, all_gids_to_retain);
 141   }
 142   hb_set_destroy (initial_gids_to_retain);
 143 
 144   _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ());
 145 
 146   glyphs->alloc (all_gids_to_retain->get_population ());
 147   gid = HB_SET_VALUE_INVALID;
 148   while (all_gids_to_retain->next (&gid))
 149     glyphs->push (gid);
 150 
 151   cff.fini ();
 152   glyf.fini ();
 153   cmap.fini ();
 154 
 155   return all_gids_to_retain;
 156 }
 157 
 158 static void
 159 _create_old_gid_to_new_gid_map (const hb_vector_t<hb_codepoint_t> &glyphs,
 160                                 hb_map_t *glyph_map)
 161 {
 162   for (unsigned int i = 0; i < glyphs.length; i++) {
 163     glyph_map->set (glyphs[i], i);
 164   }
 165 }
 166 
 167 /**
 168  * hb_subset_plan_create:
 169  * Computes a plan for subsetting the supplied face according
 170  * to a provided input. The plan describes
 171  * which tables and glyphs should be retained.
 172  *
 173  * Return value: New subset plan.
 174  *
 175  * Since: 1.7.5
 176  **/
 177 hb_subset_plan_t *
 178 hb_subset_plan_create (hb_face_t           *face,
 179                        hb_subset_input_t   *input)
 180 {
 181   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
 182 
 183   plan->drop_hints = input->drop_hints;
 184   plan->drop_layout = input->drop_layout;
 185   plan->desubroutinize = input->desubroutinize;
 186   plan->unicodes = hb_set_create();
 187   plan->glyphs.init();
 188   plan->source = hb_face_reference (face);
 189   plan->dest = hb_face_builder_create ();
 190   plan->codepoint_to_glyph = hb_map_create();
 191   plan->glyph_map = hb_map_create();
 192   plan->glyphset = _populate_gids_to_retain (face,
 193                                              input->unicodes,
 194                                              !plan->drop_layout,
 195                                              plan->unicodes,
 196                                              plan->codepoint_to_glyph,
 197                                              &plan->glyphs);
 198   _create_old_gid_to_new_gid_map (plan->glyphs,
 199                                   plan->glyph_map);
 200 
 201   return plan;
 202 }
 203 
 204 /**
 205  * hb_subset_plan_destroy:
 206  *
 207  * Since: 1.7.5
 208  **/
 209 void
 210 hb_subset_plan_destroy (hb_subset_plan_t *plan)
 211 {
 212   if (!hb_object_destroy (plan)) return;
 213 
 214   hb_set_destroy (plan->unicodes);
 215   plan->glyphs.fini ();
 216   hb_face_destroy (plan->source);
 217   hb_face_destroy (plan->dest);
 218   hb_map_destroy (plan->codepoint_to_glyph);
 219   hb_map_destroy (plan->glyph_map);
 220   hb_set_destroy (plan->glyphset);
 221 
 222   free (plan);
 223 }