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, Rod Sheeter, Behdad Esfahbod
     25  */
     26 
     27 #include "hb.hh"
     28 #include "hb-open-type.hh"
     29 
     30 #include "hb-subset.hh"
     31 #include "hb-subset-glyf.hh"
     32 
     33 #include "hb-open-file.hh"
     34 #include "hb-ot-cmap-table.hh"
     35 #include "hb-ot-glyf-table.hh"
     36 #include "hb-ot-hdmx-table.hh"
     37 #include "hb-ot-head-table.hh"
     38 #include "hb-ot-hhea-table.hh"
     39 #include "hb-ot-hmtx-table.hh"
     40 #include "hb-ot-maxp-table.hh"
     41 #include "hb-ot-os2-table.hh"
     42 #include "hb-ot-post-table.hh"
     43 #include "hb-ot-cff1-table.hh"
     44 #include "hb-ot-cff2-table.hh"
     45 #include "hb-ot-vorg-table.hh"
     46 #include "hb-ot-layout-gsub-table.hh"
     47 #include "hb-ot-layout-gpos-table.hh"
     48 
     49 
     50 static unsigned int
     51 _plan_estimate_subset_table_size (hb_subset_plan_t *plan,
     52 				  unsigned int table_len)
     53 {
     54   unsigned int src_glyphs = plan->source->get_num_glyphs ();
     55   unsigned int dst_glyphs = plan->glyphset->get_population ();
     56 
     57   if (unlikely (!src_glyphs))
     58     return 512 + table_len;
     59 
     60   return 512 + (unsigned int) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
     61 }
     62 
     63 template<typename TableType>
     64 static bool
     65 _subset2 (hb_subset_plan_t *plan)
     66 {
     67   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
     68   const TableType *table = source_blob->as<TableType> ();
     69 
     70   hb_tag_t tag = TableType::tableTag;
     71   hb_bool_t result = false;
     72   if (source_blob->data)
     73   {
     74     hb_vector_t<char> buf;
     75     unsigned int buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
     76     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
     77     if (unlikely (!buf.alloc (buf_size)))
     78     {
     79       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
     80       return false;
     81     }
     82   retry:
     83     hb_serialize_context_t serializer ((void *) buf, buf_size);
     84     hb_subset_context_t c (plan, &serializer);
     85     result = table->subset (&c);
     86     if (serializer.in_error ())
     87     {
     88       buf_size += (buf_size >> 1) + 32;
     89       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size);
     90       if (unlikely (!buf.alloc (buf_size)))
     91       {
     92 	DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size);
     93 	return false;
     94       }
     95       goto retry;
     96     }
     97     if (result)
     98     {
     99       hb_blob_t *dest_blob = serializer.copy_blob ();
    100       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
    101       result = c.plan->add_table (tag, dest_blob);
    102       hb_blob_destroy (dest_blob);
    103     }
    104     else
    105     {
    106       DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
    107       result = true;
    108     }
    109   }
    110   else
    111     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
    112 
    113   hb_blob_destroy (source_blob);
    114   DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
    115   return result;
    116 }
    117 
    118 template<typename TableType>
    119 static bool
    120 _subset (hb_subset_plan_t *plan)
    121 {
    122   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
    123   const TableType *table = source_blob->as<TableType> ();
    124 
    125   hb_tag_t tag = TableType::tableTag;
    126   hb_bool_t result = false;
    127   if (source_blob->data)
    128     result = table->subset (plan);
    129   else
    130     DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
    131 
    132   hb_blob_destroy (source_blob);
    133   DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
    134   return result;
    135 }
    136 
    137 
    138 static bool
    139 _subset_table (hb_subset_plan_t *plan,
    140 	       hb_tag_t          tag)
    141 {
    142   DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG (tag));
    143   bool result = true;
    144   switch (tag) {
    145     case HB_OT_TAG_glyf:
    146       result = _subset<const OT::glyf> (plan);
    147       break;
    148     case HB_OT_TAG_hdmx:
    149       result = _subset<const OT::hdmx> (plan);
    150       break;
    151     case HB_OT_TAG_head:
    152       // TODO that won't work well if there is no glyf
    153       DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
    154       result = true;
    155       break;
    156     case HB_OT_TAG_hhea:
    157       DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx");
    158       return true;
    159     case HB_OT_TAG_hmtx:
    160       result = _subset<const OT::hmtx> (plan);
    161       break;
    162     case HB_OT_TAG_vhea:
    163       DEBUG_MSG(SUBSET, nullptr, "skip vhea handled by vmtx");
    164       return true;
    165     case HB_OT_TAG_vmtx:
    166       result = _subset<const OT::vmtx> (plan);
    167       break;
    168     case HB_OT_TAG_maxp:
    169       result = _subset<const OT::maxp> (plan);
    170       break;
    171     case HB_OT_TAG_loca:
    172       DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf");
    173       return true;
    174     case HB_OT_TAG_cmap:
    175       result = _subset<const OT::cmap> (plan);
    176       break;
    177     case HB_OT_TAG_OS2:
    178       result = _subset<const OT::OS2> (plan);
    179       break;
    180     case HB_OT_TAG_post:
    181       result = _subset<const OT::post> (plan);
    182       break;
    183     case HB_OT_TAG_cff1:
    184       result = _subset<const OT::cff1> (plan);
    185       break;
    186     case HB_OT_TAG_cff2:
    187       result = _subset<const OT::cff2> (plan);
    188       break;
    189     case HB_OT_TAG_VORG:
    190       result = _subset<const OT::VORG> (plan);
    191       break;
    192     case HB_OT_TAG_GDEF:
    193       result = _subset2<const OT::GDEF> (plan);
    194       break;
    195     case HB_OT_TAG_GSUB:
    196       result = _subset2<const OT::GSUB> (plan);
    197       break;
    198     case HB_OT_TAG_GPOS:
    199       result = _subset2<const OT::GPOS> (plan);
    200       break;
    201 
    202     default:
    203       hb_blob_t *source_table = hb_face_reference_table (plan->source, tag);
    204       if (likely (source_table))
    205 	result = plan->add_table (tag, source_table);
    206       else
    207 	result = false;
    208       hb_blob_destroy (source_table);
    209       break;
    210   }
    211   DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG (tag), result ? "ok" : "FAILED");
    212   return result;
    213 }
    214 
    215 static bool
    216 _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
    217 {
    218   switch (tag) {
    219     case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
    220     case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */
    221     case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */
    222     case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */
    223     case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */
    224     case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
    225       return plan->drop_hints;
    226     // Drop Layout Tables if requested.
    227     case HB_OT_TAG_GDEF:
    228     case HB_OT_TAG_GPOS:
    229     case HB_OT_TAG_GSUB:
    230       return plan->drop_layout;
    231     // Drop these tables below by default, list pulled
    232     // from fontTools:
    233     case HB_TAG ('B', 'A', 'S', 'E'):
    234     case HB_TAG ('J', 'S', 'T', 'F'):
    235     case HB_TAG ('D', 'S', 'I', 'G'):
    236     case HB_TAG ('E', 'B', 'D', 'T'):
    237     case HB_TAG ('E', 'B', 'L', 'C'):
    238     case HB_TAG ('E', 'B', 'S', 'C'):
    239     case HB_TAG ('S', 'V', 'G', ' '):
    240     case HB_TAG ('P', 'C', 'L', 'T'):
    241     case HB_TAG ('L', 'T', 'S', 'H'):
    242     // Graphite tables:
    243     case HB_TAG ('F', 'e', 'a', 't'):
    244     case HB_TAG ('G', 'l', 'a', 't'):
    245     case HB_TAG ('G', 'l', 'o', 'c'):
    246     case HB_TAG ('S', 'i', 'l', 'f'):
    247     case HB_TAG ('S', 'i', 'l', 'l'):
    248     // Colour
    249     case HB_TAG ('s', 'b', 'i', 'x'):
    250       return true;
    251     default:
    252       return false;
    253   }
    254 }
    255 
    256 /**
    257  * hb_subset:
    258  * @source: font face data to be subset.
    259  * @input: input to use for the subsetting.
    260  *
    261  * Subsets a font according to provided input.
    262  **/
    263 hb_face_t *
    264 hb_subset (hb_face_t *source,
    265 	   hb_subset_input_t *input)
    266 {
    267   if (unlikely (!input || !source)) return hb_face_get_empty ();
    268 
    269   hb_subset_plan_t *plan = hb_subset_plan_create (source, input);
    270 
    271   hb_tag_t table_tags[32];
    272   unsigned int offset = 0, count;
    273   bool success = true;
    274   do {
    275     count = ARRAY_LENGTH (table_tags);
    276     hb_face_get_table_tags (source, offset, &count, table_tags);
    277     for (unsigned int i = 0; i < count; i++)
    278     {
    279       hb_tag_t tag = table_tags[i];
    280       if (_should_drop_table (plan, tag))
    281       {
    282 	DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag));
    283 	continue;
    284       }
    285       success = success && _subset_table (plan, tag);
    286     }
    287     offset += count;
    288   } while (success && count == ARRAY_LENGTH (table_tags));
    289 
    290   hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty ();
    291   hb_subset_plan_destroy (plan);
    292   return result;
    293 }
    294