Home | History | Annotate | Download | only in src
      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.len; 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 }
    224