1 /*
   2  * Copyright © 2018 Adobe 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  * Adobe Author(s): Michiharu Ariza
  25  */
  26 
  27 #include "hb-ot-cff-common.hh"
  28 #include "hb-ot-cff2-table.hh"
  29 #include "hb-subset-cff-common.hh"
  30 
  31 /* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it.
  32  * Rarely any/much smaller than format 3 anyway. */
  33 #define CFF_SERIALIZE_FDSELECT_0  0
  34 
  35 using namespace CFF;
  36 
  37 /**
  38  * hb_plan_subset_cff_fdselect
  39  * Determine an optimal FDSelect format according to a provided plan.
  40  *
  41  * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
  42  * along with a font index remapping table
  43  **/
  44 
  45 bool
  46 hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
  47                             unsigned int fdCount,
  48                             const FDSelect &src, /* IN */
  49                             unsigned int &subset_fd_count /* OUT */,
  50                             unsigned int &subset_fdselect_size /* OUT */,
  51                             unsigned int &subset_fdselect_format /* OUT */,
  52                             hb_vector_t<code_pair_t> &fdselect_ranges /* OUT */,
  53                             remap_t &fdmap /* OUT */)
  54 {
  55   subset_fd_count = 0;
  56   subset_fdselect_size = 0;
  57   subset_fdselect_format = 0;
  58   unsigned int  num_ranges = 0;
  59 
  60   unsigned int subset_num_glyphs = glyphs.length;
  61   if (subset_num_glyphs == 0)
  62     return true;
  63 
  64   {
  65     /* use hb_set to determine the subset of font dicts */
  66     hb_set_t  *set = hb_set_create ();
  67     if (set == &Null (hb_set_t))
  68       return false;
  69     hb_codepoint_t  prev_fd = CFF_UNDEF_CODE;
  70     for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
  71     {
  72       hb_codepoint_t  fd = src.get_fd (glyphs[i]);
  73       set->add (fd);
  74 
  75       if (fd != prev_fd)
  76       {
  77         num_ranges++;
  78         prev_fd = fd;
  79         code_pair_t pair = { fd, i };
  80         fdselect_ranges.push (pair);
  81       }
  82     }
  83 
  84     subset_fd_count = set->get_population ();
  85     if (subset_fd_count == fdCount)
  86     {
  87       /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
  88       fdmap.identity (fdCount);
  89       hb_set_destroy (set);
  90     }
  91     else
  92     {
  93       /* create a fdmap */
  94       if (!fdmap.reset (fdCount))
  95       {
  96         hb_set_destroy (set);
  97         return false;
  98       }
  99 
 100       hb_codepoint_t  fd = CFF_UNDEF_CODE;
 101       while (set->next (&fd))
 102         fdmap.add (fd);
 103       hb_set_destroy (set);
 104       if (unlikely (fdmap.get_count () != subset_fd_count))
 105         return false;
 106     }
 107 
 108     /* update each font dict index stored as "code" in fdselect_ranges */
 109     for (unsigned int i = 0; i < fdselect_ranges.length; i++)
 110       fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code];
 111   }
 112 
 113   /* determine which FDSelect format is most compact */
 114   if (subset_fd_count > 0xFF)
 115   {
 116     if (unlikely (src.format != 4))
 117       return false;
 118     subset_fdselect_format = 4;
 119     subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size;
 120   }
 121   else
 122   {
 123 #if CFF_SERIALIZE_FDSELECT_0
 124     unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
 125 #endif
 126     unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size;
 127 
 128 #if CFF_SERIALIZE_FDSELECT_0
 129     if (format0_size <= format3_size)
 130     {
 131       // subset_fdselect_format = 0;
 132       subset_fdselect_size = format0_size;
 133     }
 134     else
 135 #endif
 136     {
 137       subset_fdselect_format = 3;
 138       subset_fdselect_size = format3_size;
 139     }
 140   }
 141 
 142   return true;
 143 }
 144 
 145 template <typename FDSELECT3_4>
 146 static inline bool
 147 serialize_fdselect_3_4 (hb_serialize_context_t *c,
 148                           const unsigned int num_glyphs,
 149                           const FDSelect &src,
 150                           unsigned int size,
 151                           const hb_vector_t<code_pair_t> &fdselect_ranges)
 152 {
 153   TRACE_SERIALIZE (this);
 154   FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
 155   if (unlikely (p == nullptr)) return_trace (false);
 156   p->nRanges ().set (fdselect_ranges.length);
 157   for (unsigned int i = 0; i < fdselect_ranges.length; i++)
 158   {
 159     p->ranges[i].first.set (fdselect_ranges[i].glyph);
 160     p->ranges[i].fd.set (fdselect_ranges[i].code);
 161   }
 162   p->sentinel().set (num_glyphs);
 163   return_trace (true);
 164 }
 165 
 166 /**
 167  * hb_serialize_cff_fdselect
 168  * Serialize a subset FDSelect format planned above.
 169  **/
 170 bool
 171 hb_serialize_cff_fdselect (hb_serialize_context_t *c,
 172                           const unsigned int num_glyphs,
 173                           const FDSelect &src,
 174                           unsigned int fd_count,
 175                           unsigned int fdselect_format,
 176                           unsigned int size,
 177                           const hb_vector_t<code_pair_t> &fdselect_ranges)
 178 {
 179   TRACE_SERIALIZE (this);
 180   FDSelect  *p = c->allocate_min<FDSelect> ();
 181   if (unlikely (p == nullptr)) return_trace (false);
 182   p->format.set (fdselect_format);
 183   size -= FDSelect::min_size;
 184 
 185   switch (fdselect_format)
 186   {
 187 #if CFF_SERIALIZE_FDSELECT_0
 188     case 0:
 189     {
 190       FDSelect0 *p = c->allocate_size<FDSelect0> (size);
 191       if (unlikely (p == nullptr)) return_trace (false);
 192       unsigned int range_index = 0;
 193       unsigned int  fd = fdselect_ranges[range_index++].code;
 194       for (unsigned int i = 0; i < num_glyphs; i++)
 195       {
 196         if ((range_index < fdselect_ranges.len) &&
 197             (i >= fdselect_ranges[range_index].glyph))
 198         {
 199           fd = fdselect_ranges[range_index++].code;
 200         }
 201         p->fds[i].set (fd);
 202       }
 203       break;
 204     }
 205 #endif /* CFF_SERIALIZE_FDSELECT_0 */
 206 
 207     case 3:
 208       return serialize_fdselect_3_4<FDSelect3> (c,
 209                                                 num_glyphs,
 210                                                 src,
 211                                                 size,
 212                                                 fdselect_ranges);
 213 
 214     case 4:
 215       return serialize_fdselect_3_4<FDSelect4> (c,
 216                                                 num_glyphs,
 217                                                 src,
 218                                                 size,
 219                                                 fdselect_ranges);
 220 
 221     default:
 222       assert(false);
 223   }
 224 
 225   return_trace (true);
 226 }