src/java.desktop/share/native/libfontmanager/harfbuzz/hb-subset-glyf.cc
changeset 54232 7c11a7cc7c1d
equal deleted inserted replaced
54231:e4813eded7cb 54232:7c11a7cc7c1d
       
     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-open-type.hh"
       
    28 #include "hb-ot-glyf-table.hh"
       
    29 #include "hb-set.h"
       
    30 #include "hb-subset-glyf.hh"
       
    31 
       
    32 static bool
       
    33 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
       
    34                                      hb_vector_t<hb_codepoint_t> &glyph_ids,
       
    35                                      hb_bool_t drop_hints,
       
    36                                      bool *use_short_loca /* OUT */,
       
    37                                      unsigned int *glyf_size /* OUT */,
       
    38                                      unsigned int *loca_size /* OUT */,
       
    39                                      hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
       
    40 {
       
    41   unsigned int total = 0;
       
    42   for (unsigned int i = 0; i < glyph_ids.length; i++)
       
    43   {
       
    44     hb_codepoint_t next_glyph = glyph_ids[i];
       
    45     if (!instruction_ranges->resize (instruction_ranges->length + 2))
       
    46     {
       
    47       DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
       
    48       return false;
       
    49     }
       
    50     unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
       
    51     *instruction_start = 0;
       
    52     unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
       
    53     *instruction_end = 0;
       
    54 
       
    55     unsigned int start_offset, end_offset;
       
    56     if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) &&
       
    57                     glyf.remove_padding (start_offset, &end_offset))))
       
    58     {
       
    59       DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph);
       
    60       continue;
       
    61     }
       
    62     if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size)
       
    63       continue; /* 0-length glyph */
       
    64 
       
    65     if (drop_hints)
       
    66     {
       
    67       if (unlikely (!glyf.get_instruction_offsets (start_offset, end_offset,
       
    68                                                    instruction_start, instruction_end)))
       
    69       {
       
    70         DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph);
       
    71         return false;
       
    72       }
       
    73     }
       
    74 
       
    75     total += end_offset - start_offset - (*instruction_end - *instruction_start);
       
    76     /* round2 so short loca will work */
       
    77     total += total % 2;
       
    78   }
       
    79 
       
    80   *glyf_size = total;
       
    81   *use_short_loca = (total <= 131070);
       
    82   *loca_size = (glyph_ids.length + 1)
       
    83       * (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
       
    84 
       
    85   DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca",
       
    86             total,
       
    87             *loca_size,
       
    88             *use_short_loca ? "short" : "long");
       
    89   return true;
       
    90 }
       
    91 
       
    92 static bool
       
    93 _write_loca_entry (unsigned int  id,
       
    94                    unsigned int  offset,
       
    95                    bool          is_short,
       
    96                    void         *loca_prime,
       
    97                    unsigned int  loca_size)
       
    98 {
       
    99   unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
       
   100   if ((id + 1) * entry_size <= loca_size)
       
   101   {
       
   102     if (is_short) {
       
   103       ((OT::HBUINT16*) loca_prime) [id].set (offset / 2);
       
   104     } else {
       
   105       ((OT::HBUINT32*) loca_prime) [id].set (offset);
       
   106     }
       
   107     return true;
       
   108   }
       
   109 
       
   110   // Offset was not written because the write is out of bounds.
       
   111   DEBUG_MSG(SUBSET,
       
   112             nullptr,
       
   113             "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
       
   114             id,
       
   115             loca_size);
       
   116   return false;
       
   117 }
       
   118 
       
   119 static void
       
   120 _update_components (hb_subset_plan_t * plan,
       
   121                     char * glyph_start,
       
   122                     unsigned int length)
       
   123 {
       
   124   OT::glyf::CompositeGlyphHeader::Iterator iterator;
       
   125   if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start,
       
   126                                                     length,
       
   127                                                     &iterator))
       
   128   {
       
   129     do
       
   130     {
       
   131       hb_codepoint_t new_gid;
       
   132       if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex,
       
   133                                       &new_gid))
       
   134         continue;
       
   135 
       
   136       ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid);
       
   137     } while (iterator.move_to_next ());
       
   138   }
       
   139 }
       
   140 
       
   141 static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int length)
       
   142 {
       
   143   /* remove WE_HAVE_INSTRUCTIONS from flags in dest */
       
   144   OT::glyf::CompositeGlyphHeader::Iterator composite_it;
       
   145   if (unlikely (!OT::glyf::CompositeGlyphHeader::get_iterator (glyf_prime, length, &composite_it))) return false;
       
   146   const OT::glyf::CompositeGlyphHeader *glyph;
       
   147   do {
       
   148     glyph = composite_it.current;
       
   149     OT::HBUINT16 *flags = const_cast<OT::HBUINT16 *> (&glyph->flags);
       
   150     flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS);
       
   151   } while (composite_it.move_to_next ());
       
   152   return true;
       
   153 }
       
   154 
       
   155 static bool
       
   156 _write_glyf_and_loca_prime (hb_subset_plan_t              *plan,
       
   157                             const OT::glyf::accelerator_t &glyf,
       
   158                             const char                    *glyf_data,
       
   159                             bool                           use_short_loca,
       
   160                             hb_vector_t<unsigned int> &instruction_ranges,
       
   161                             unsigned int                   glyf_prime_size,
       
   162                             char                          *glyf_prime_data /* OUT */,
       
   163                             unsigned int                   loca_prime_size,
       
   164                             char                          *loca_prime_data /* OUT */)
       
   165 {
       
   166   hb_vector_t<hb_codepoint_t> &glyph_ids = plan->glyphs;
       
   167   char *glyf_prime_data_next = glyf_prime_data;
       
   168 
       
   169   bool success = true;
       
   170   for (unsigned int i = 0; i < glyph_ids.length; i++)
       
   171   {
       
   172     unsigned int start_offset, end_offset;
       
   173     if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) &&
       
   174                     glyf.remove_padding (start_offset, &end_offset))))
       
   175       end_offset = start_offset = 0;
       
   176 
       
   177     unsigned int instruction_start = instruction_ranges[i * 2];
       
   178     unsigned int instruction_end = instruction_ranges[i * 2 + 1];
       
   179 
       
   180     int length = end_offset - start_offset - (instruction_end - instruction_start);
       
   181 
       
   182     if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size)
       
   183     {
       
   184       DEBUG_MSG(SUBSET,
       
   185                  nullptr,
       
   186                  "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
       
   187                  i, length);
       
   188       return false;
       
   189     }
       
   190 
       
   191     if (instruction_start == instruction_end)
       
   192       memcpy (glyf_prime_data_next, glyf_data + start_offset, length);
       
   193     else
       
   194     {
       
   195       memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset);
       
   196       memcpy (glyf_prime_data_next + instruction_start - start_offset, glyf_data + instruction_end, end_offset - instruction_end);
       
   197       /* if the instructions end at the end this was a composite glyph, else simple */
       
   198       if (instruction_end == end_offset)
       
   199       {
       
   200         if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false;
       
   201       }
       
   202       else
       
   203         /* zero instruction length, which is just before instruction_start */
       
   204         memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);
       
   205     }
       
   206 
       
   207     success = success && _write_loca_entry (i,
       
   208                                             glyf_prime_data_next - glyf_prime_data,
       
   209                                             use_short_loca,
       
   210                                             loca_prime_data,
       
   211                                             loca_prime_size);
       
   212     _update_components (plan, glyf_prime_data_next, length);
       
   213 
       
   214     // TODO: don't align to two bytes if using long loca.
       
   215     glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca.
       
   216   }
       
   217 
       
   218   success = success && _write_loca_entry (glyph_ids.length,
       
   219                                           glyf_prime_data_next - glyf_prime_data,
       
   220                                           use_short_loca,
       
   221                                           loca_prime_data,
       
   222                                           loca_prime_size);
       
   223   return success;
       
   224 }
       
   225 
       
   226 static bool
       
   227 _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
       
   228                           const char                     *glyf_data,
       
   229                           hb_subset_plan_t               *plan,
       
   230                           bool                           *use_short_loca,
       
   231                           hb_blob_t                     **glyf_prime /* OUT */,
       
   232                           hb_blob_t                     **loca_prime /* OUT */)
       
   233 {
       
   234   // TODO(grieger): Sanity check allocation size for the new table.
       
   235   hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
       
   236 
       
   237   unsigned int glyf_prime_size;
       
   238   unsigned int loca_prime_size;
       
   239   hb_vector_t<unsigned int> instruction_ranges;
       
   240   instruction_ranges.init ();
       
   241 
       
   242   if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
       
   243                                                       glyphs_to_retain,
       
   244                                                       plan->drop_hints,
       
   245                                                       use_short_loca,
       
   246                                                       &glyf_prime_size,
       
   247                                                       &loca_prime_size,
       
   248                                                       &instruction_ranges))) {
       
   249     instruction_ranges.fini ();
       
   250     return false;
       
   251   }
       
   252 
       
   253   char *glyf_prime_data = (char *) calloc (1, glyf_prime_size);
       
   254   char *loca_prime_data = (char *) calloc (1, loca_prime_size);
       
   255   if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data,
       
   256                                              *use_short_loca,
       
   257                                              instruction_ranges,
       
   258                                              glyf_prime_size, glyf_prime_data,
       
   259                                              loca_prime_size, loca_prime_data))) {
       
   260     free (glyf_prime_data);
       
   261     free (loca_prime_data);
       
   262     instruction_ranges.fini ();
       
   263     return false;
       
   264   }
       
   265   instruction_ranges.fini ();
       
   266 
       
   267   *glyf_prime = hb_blob_create (glyf_prime_data,
       
   268                                 glyf_prime_size,
       
   269                                 HB_MEMORY_MODE_READONLY,
       
   270                                 glyf_prime_data,
       
   271                                 free);
       
   272   *loca_prime = hb_blob_create (loca_prime_data,
       
   273                                 loca_prime_size,
       
   274                                 HB_MEMORY_MODE_READONLY,
       
   275                                 loca_prime_data,
       
   276                                 free);
       
   277   return true;
       
   278 }
       
   279 
       
   280 /**
       
   281  * hb_subset_glyf:
       
   282  * Subsets the glyph table according to a provided plan.
       
   283  *
       
   284  * Return value: subsetted glyf table.
       
   285  *
       
   286  * Since: 1.7.5
       
   287  **/
       
   288 bool
       
   289 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
       
   290                          bool             *use_short_loca, /* OUT */
       
   291                          hb_blob_t       **glyf_prime, /* OUT */
       
   292                          hb_blob_t       **loca_prime /* OUT */)
       
   293 {
       
   294   hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table<OT::glyf> (plan->source);
       
   295   const char *glyf_data = hb_blob_get_data (glyf_blob, nullptr);
       
   296 
       
   297   OT::glyf::accelerator_t glyf;
       
   298   glyf.init (plan->source);
       
   299   bool result = _hb_subset_glyf_and_loca (glyf,
       
   300                                           glyf_data,
       
   301                                           plan,
       
   302                                           use_short_loca,
       
   303                                           glyf_prime,
       
   304                                           loca_prime);
       
   305 
       
   306   hb_blob_destroy (glyf_blob);
       
   307   glyf.fini ();
       
   308 
       
   309   return result;
       
   310 }