subset FDSelect and FDArray
authorMichiharu Ariza <ariza@adobe.com>
Mon, 6 Aug 2018 17:04:53 +0000 (10:04 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Mon, 6 Aug 2018 17:04:53 +0000 (10:04 -0700)
Added a set of sources hb-subset-cff-common-private.cc & .hh for FDSelect subseting code.
Added FDSelect format 4 (CFF2 only) support. Shared its implementation with format 3 as a template.

src/Makefile.sources
src/hb-ot-cff-common-private.hh
src/hb-ot-cff2-table.hh
src/hb-subset-cff-common-private.cc [new file with mode: 0644]
src/hb-subset-cff-common-private.hh [new file with mode: 0644]
src/hb-subset-cff2.cc

index 1d6374e..6779002 100644 (file)
@@ -209,6 +209,7 @@ HB_SUBSET_sources = \
        hb-subset.cc \
        hb-subset-glyf.cc \
        hb-subset-cff2.cc \
+       hb-subset-cff-common-private.cc \
        hb-subset-input.cc \
        hb-subset-plan.cc \
        $(NULL)
@@ -217,6 +218,7 @@ HB_SUBSET_headers = \
        hb-subset.h \
        hb-subset-glyf.hh \
        hb-subset-cff2.hh \
+       hb-subset-cff-common-private.hh \
        hb-subset-plan.hh \
        hb-subset-private.hh \
        $(NULL)
index 97565c7..3690a85 100644 (file)
@@ -522,33 +522,37 @@ struct FDArray : IndexOf<FontDict>
   inline bool serialize (hb_serialize_context_t *c,
                         unsigned int offSize,
                         const hb_vector_t<DICTVAL> &fontDicts,
+                        unsigned int fdCount,
+                        const hb_vector_t<hb_codepoint_t> &fdmap,
                         OP_SERIALIZER& opszr,
                         const hb_vector_t<offset_size_pair> &privatePairs)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    this->count.set (fontDicts.len);
+    this->count.set (fdCount);
     this->offSize.set (offSize);
-    if (!unlikely (c->allocate_size<HBUINT8> (offSize * (fontDicts.len + 1))))
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize * (fdCount + 1))))
       return_trace (false);
     
     /* serialize font dict offsets */
     unsigned int  offset = 1;
-    unsigned int  i;
-    for (i = 0; i < fontDicts.len; i++)
-    {
-      set_offset_at (i, offset);
-      offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
-    }
-    set_offset_at (i, offset);
+    unsigned int  fid = 0;
+    for (unsigned i = 0; i < fontDicts.len; i++)
+      if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID)
+      {
+        set_offset_at (fid++, offset);
+        offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+      }
+    set_offset_at (fid, offset);
 
     /* serialize font dicts */
     for (unsigned int i = 0; i < fontDicts.len; i++)
-    {
-      FontDict *dict = c->start_embed<FontDict> ();
-      if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privatePairs[i])))
-        return_trace (false);
-    }
+      if (fdmap[i] != HB_SET_VALUE_INVALID)
+      {
+        FontDict *dict = c->start_embed<FontDict> ();
+        if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privatePairs[i])))
+          return_trace (false);
+      }
     return_trace (true);
   }
   
@@ -556,23 +560,37 @@ struct FDArray : IndexOf<FontDict>
   template <typename OP_SERIALIZER, typename DICTVAL>
   inline static unsigned int calculate_serialized_size (unsigned int &offSize /* OUT */,
                                                         const hb_vector_t<DICTVAL> &fontDicts,
+                                                        unsigned int fdCount,
+                                                        const hb_vector_t<hb_codepoint_t> &fdmap,
                                                         OP_SERIALIZER& opszr)
   {
     unsigned int dictsSize = 0;
     for (unsigned int i = 0; i < fontDicts.len; i++)
-      dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+      if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID)
+        dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
 
     offSize = calcOffSize (dictsSize + 1);
-    return Index::calculate_serialized_size (offSize, fontDicts.len, dictsSize);
+    return Index::calculate_serialized_size (offSize, fdCount, dictsSize);
   }
 };
 
 /* FDSelect */
 struct FDSelect0 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && fds[c->get_num_glyphs () - 1].sanitize (c)));
+    if (unlikely (!(c->check_struct (this))))
+      return_trace (false);
+    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
+      if (unlikely (!fds[i].sanitize (c)))
+        return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    return (hb_codepoint_t)fds[glyph];
   }
 
   inline unsigned int get_size (unsigned int num_glyphs) const
@@ -583,44 +601,75 @@ struct FDSelect0 {
   DEFINE_SIZE_MIN (1);
 };
 
-struct FDSelect3_Range {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4_Range {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ())));
+    return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
   }
 
-  HBUINT16    first;
-  HBUINT8     fd;
+  GID_TYPE    first;
+  FD_TYPE     fd;
 
-  DEFINE_SIZE_STATIC (3);
+  DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
 };
 
-struct FDSelect3 {
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4 {
   inline unsigned int get_size (void) const
-  { return HBUINT16::static_size * 2 + FDSelect3_Range::static_size * nRanges; }
+  { return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && (nRanges > 0) &&
-                         (ranges[nRanges - 1].sanitize (c))));
+    if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < nRanges; i++)
+    {
+      if (unlikely (!ranges[i].sanitize (c, fdcount)))
+        return_trace (false);
+      if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
+        return_trace (false);
+    }
+    if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
+      return_trace (false);
+
+    return_trace (true);
   }
 
-  HBUINT16         nRanges;
-  FDSelect3_Range  ranges[VAR];
-  /* HBUINT16 sentinel */
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    for (unsigned int i = 0; i < nRanges; i++)
+      if (glyph < ranges[i + 1].first)
+        return (hb_codepoint_t)ranges[i].fd;
+
+    assert (false);
+  }
 
-  DEFINE_SIZE_MIN (5);
+  inline GID_TYPE &sentinel (void)  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+  inline const GID_TYPE &sentinel (void) const  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+
+  GID_TYPE         nRanges;
+  FDSelect3_4_Range<GID_TYPE, FD_TYPE>  ranges[VAR];
+  /* GID_TYPE sentinel */
+
+  DEFINE_SIZE_ARRAY (GID_TYPE::static_size * 2, ranges);
 };
 
+typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
+typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
+
 struct FDSelect {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
   {
     TRACE_SANITIZE (this);
 
     return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
-                          (format == 0)? u.format0.sanitize (c): u.format3.sanitize (c)));
+                          (format == 0)?
+                          u.format0.sanitize (c, fdcount):
+                          u.format3.sanitize (c, fdcount)));
   }
 
   inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
@@ -641,11 +690,19 @@ struct FDSelect {
     unsigned int size = format.static_size;
     if (format == 0)
       size += u.format0.get_size (num_glyphs);
-    else if (likely (format == 3))
+    else
       size += u.format3.get_size ();
     return size;
   }
 
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else
+      return u.format3.get_fd (glyph);
+  }
+
   HBUINT8       format;
   union {
     FDSelect0   format0;
@@ -872,6 +929,7 @@ struct SubTableOffsets {
   unsigned int  topDictSize;
   unsigned int  varStoreOffset;
   unsigned int  FDSelectOffset;
+  unsigned int  FDSelectSize;
   unsigned int  FDArrayOffset;
   unsigned int  FDArrayOffSize;
   unsigned int  charStringsOffset;
index f0a40a8..a161189 100644 (file)
@@ -38,6 +38,68 @@ namespace CFF {
  */
 #define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
 
+typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
+typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
+
+struct CFF2FDSelect
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) &&
+                          (format == 0)?
+                          u.format0.sanitize (c, fdcount):
+                            ((format == 3)?
+                            u.format3.sanitize (c, fdcount):
+                            u.format4.sanitize (c, fdcount))));
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+  { return get_size (num_glyphs); }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = format.static_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else if (format == 3)
+      size += u.format3.get_size ();
+    else
+      size += u.format4.get_size ();
+    return size;
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else if (format == 3)
+      return u.format3.get_fd (glyph);
+    else
+      return u.format4.get_fd (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+    FDSelect4   format4;
+  } u;
+
+  DEFINE_SIZE_MIN (2);
+};
+
 struct CFF2VariationStore
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -100,7 +162,7 @@ struct CFF2TopDictValues : DictValues<OpStr>
   LOffsetTo<CharStrings>        charStringsOffset;
   LOffsetTo<CFF2VariationStore> vstoreOffset;
   LOffsetTo<FDArray>            FDArrayOffset;
-  LOffsetTo<FDSelect>           FDSelectOffset;
+  LOffsetTo<CFF2FDSelect>       FDSelectOffset;
 };
 
 struct CFF2TopDictOpSet
@@ -414,7 +476,7 @@ struct cff2
       if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
           ((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&sc))) ||
           ((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&sc))) ||
-          ((fdSelect != &Null(FDSelect)) && unlikely (!fdSelect->sanitize (&sc))))
+          ((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))
       {
         fini ();
         return;
@@ -453,7 +515,8 @@ struct cff2
         }
 
         privateDicts[i].localSubrs = &privateDicts[i].subrsOffset (privDictStr.str);
-        if (unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
+        if (privateDicts[i].localSubrs != &Null(Subrs) &&
+          unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
         {
           fini ();
           return;
@@ -492,7 +555,7 @@ struct cff2
     const CFF2VariationStore  *varStore;
     const CharStrings         *charStrings;
     const FDArray             *fdArray;
-    const FDSelect            *fdSelect;
+    const CFF2FDSelect        *fdSelect;
 
     hb_vector_t<CFF2FontDictValues>     fontDicts;
     hb_vector_t<PrivDictVal>  privateDicts;
diff --git a/src/hb-subset-cff-common-private.cc b/src/hb-subset-cff-common-private.cc
new file mode 100644 (file)
index 0000000..29ec15b
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-ot-cff-common-private.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-subset-cff-common-private.hh"
+
+using namespace CFF;
+
+/**
+ * hb_plan_subset_cff_fdselect
+ * Determine an optimal FDSelect format according to a provided plan.
+ *
+ * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
+ * along with a font index remapping table
+ **/
+
+bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+                            unsigned int fdCount,
+                            const CFF2FDSelect &src, /* IN */
+                            unsigned int &subset_fd_count /* OUT */,
+                            unsigned int &subst_fdselect_size /* OUT */,
+                            unsigned int &subst_fdselect_format /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &fdmap /* OUT */)
+{
+  subset_fd_count = 0;
+  subst_fdselect_size = 0;
+  subst_fdselect_format = 0;
+  unsigned int  num_ranges = 0;
+
+  unsigned int subset_num_glyphs = glyphs.len;
+  if (subset_num_glyphs == 0)
+    return true;
+
+  {
+    /* use hb_set to determine the subset of font dicts */
+    hb_set_t  *set = hb_set_create ();
+    if (set == &Null (hb_set_t))
+      return false;
+    hb_codepoint_t  prev_fd = HB_SET_VALUE_INVALID;
+    for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+    {
+      hb_codepoint_t  fd = src.get_fd (glyphs[i]);
+      set->add (fd);
+      
+      if (fd != prev_fd)
+      {
+        num_ranges++;
+        prev_fd = fd;
+        subst_first_glyphs.push (i);
+      }
+    }
+
+    if (set->get_population () == fdCount)
+    {
+      /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
+      hb_set_destroy (set);
+      return true;
+    }
+
+    /* create a fdmap */
+    fdmap.resize (fdCount);
+    for (unsigned int i = 0; i < fdmap.len; i++)
+      fdmap[i] = HB_SET_VALUE_INVALID;
+    hb_codepoint_t  fd = HB_SET_VALUE_INVALID;
+    while (set->next (&fd))
+      fdmap[fd] = subset_fd_count++;
+    assert (subset_fd_count == set->get_population ());
+    hb_set_destroy (set);
+  }
+
+  /* determine which FDSelect format is most compact */
+  if (subset_fd_count > 0xFF)
+  {
+    assert (src.format == 4);
+    subst_fdselect_format = 4;
+    subst_fdselect_size = FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges;
+  }
+  else
+  {
+    unsigned int format0_size = FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
+    unsigned int format3_size = FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges;
+
+    if (format0_size <= format3_size)
+    {
+      // subst_fdselect_format = 0;
+      subst_fdselect_size = format0_size;
+      subst_first_glyphs.fini ();
+    }
+    else
+    {
+      subst_fdselect_format = 3;
+      subst_fdselect_size = format3_size;
+    }
+  }
+
+  return true;
+}
+
+template <typename FDSELECT3_4>
+static inline bool
+serialize_fdselect_3_4 (hb_serialize_context_t *c,
+                          unsigned int num_glyphs,
+                          const CFF2FDSelect &src,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const hb_vector_t<hb_codepoint_t> &fdmap)
+{
+  TRACE_SERIALIZE (this);
+  FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->nRanges.set (first_glyphs.len);
+  for (unsigned int i = 0; i < first_glyphs.len; i++)
+  {
+    hb_codepoint_t  glyph = first_glyphs[i];
+    p->ranges[i].first.set (glyph);
+    p->ranges[i].fd.set (fdmap[src.get_fd (glyph)]);
+  }
+  p->sentinel().set (num_glyphs);
+  return_trace (true);
+}
+
+/**
+ * hb_serialize_cff_fdselect
+ * Serialize a subset FDSelect format planned above.
+ **/
+bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+                          const hb_vector_t<hb_codepoint_t> &glyphs,
+                          const CFF2FDSelect &src,
+                          unsigned int fd_count,
+                          unsigned int fdselect_format,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const hb_vector_t<hb_codepoint_t> &fdmap)
+{
+  TRACE_SERIALIZE (this);
+  FDSelect  *p = c->allocate_min<FDSelect> ();
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->format.set (fdselect_format);
+  size -= FDSelect::min_size;
+
+  switch (fdselect_format)
+  {
+    case 0:
+    {
+      FDSelect0 *p = c->allocate_size<FDSelect0> (size);
+      if (unlikely (p == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < glyphs.len; i++)
+        p->fds[i].set (fdmap[src.get_fd (glyphs[i])]);
+      break;
+    }
+    
+    case 3:
+      return serialize_fdselect_3_4<FDSelect3> (c,
+                                                glyphs.len,
+                                                src,
+                                                size,
+                                                first_glyphs,
+                                                fdmap);
+    
+    case 4:
+      return serialize_fdselect_3_4<FDSelect4> (c,
+                                                glyphs.len,
+                                                src,
+                                                size,
+                                                first_glyphs,
+                                                fdmap);
+
+    default:
+      assert(false);
+  }
+  
+  return_trace (true);
+}
diff --git a/src/hb-subset-cff-common-private.hh b/src/hb-subset-cff-common-private.hh
new file mode 100644 (file)
index 0000000..ff4b2d5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF_COMMON_PRIVATE_HH
+#define HB_SUBSET_CFF_COMMON_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+                            unsigned int fdCount,
+                            const CFF::CFF2FDSelect &src, /* IN */
+                            unsigned int &subset_fd_count /* OUT */,
+                            unsigned int &subst_fdselect_size /* OUT */,
+                            unsigned int &subst_fdselect_format /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &fdmap /* OUT */);
+
+HB_INTERNAL bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+                          const hb_vector_t<hb_codepoint_t> &glyphs,
+                          const CFF::CFF2FDSelect &src,
+                          unsigned int fd_count,
+                          unsigned int fdselect_format,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const hb_vector_t<hb_codepoint_t> &fdmap);
+
+#endif /* HB_SUBSET_CFF_COMMON_PRIVATE_HH */
index c2c012c..a2f1a89 100644 (file)
@@ -29,6 +29,7 @@
 #include "hb-set.h"
 #include "hb-subset-cff2.hh"
 #include "hb-subset-plan.hh"
+#include "hb-subset-cff-common-private.hh"
 
 using namespace CFF;
 
@@ -144,14 +145,19 @@ struct CFF2PrivateDict_OpSerializer : OpSerializer
 
 struct subset_plan {
   inline subset_plan (void)
-    : final_size (0)
+    : final_size (0),
+      subst_fdcount(1)
   {
+    subst_fdselect_first_glyphs.init ();
+    fdmap.init ();
     subset_charstrings.init ();
     private_off_and_size_pairs.init ();
   }
 
   inline ~subset_plan (void)
   {
+    subst_fdselect_first_glyphs.fini ();
+    fdmap.fini ();
     subset_charstrings.fini ();
     private_off_and_size_pairs.fini ();
   }
@@ -160,6 +166,7 @@ struct subset_plan {
               hb_subset_plan_t *plan)
   {
     final_size = 0;
+    orig_fdcount = acc.fdArray->count;
 
     /* CFF2 header */
     final_size += OT::cff2::static_size;
@@ -182,17 +189,29 @@ struct subset_plan {
     }
 
     /* FDSelect */
-    if (acc.fdSelect != &Null(FDSelect))
+    if (acc.fdSelect != &Null(CFF2FDSelect))
     {
       offsets.FDSelectOffset = final_size;
-      final_size += acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+      if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+                                  orig_fdcount,
+                                  *acc.fdSelect,
+                                  subst_fdcount,
+                                  offsets.FDSelectSize,
+                                  subst_fdselect_format,
+                                  subst_fdselect_first_glyphs,
+                                  fdmap)))
+        return false;
+      
+      if (!is_fds_subsetted ())
+        offsets.FDSelectSize = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+      final_size += offsets.FDSelectSize;
     }
 
     /* FDArray (FDIndex) */
     {
       offsets.FDArrayOffset = final_size;
       CFF2FontDict_OpSerializer fontSzr;
-      final_size += FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, fontSzr);
+      final_size += FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
     }
 
     /* CharStrings */
@@ -211,7 +230,7 @@ struct subset_plan {
 
     /* private dicts & local subrs */
     offsets.privateDictsOffset = final_size;
-    for (unsigned int i = 0; i < acc.fdArray->count; i++)
+    for (unsigned int i = 0; i < orig_fdcount; i++)
     {
       CFF2PrivateDict_OpSerializer privSzr;
       unsigned int private_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
@@ -228,12 +247,23 @@ struct subset_plan {
   unsigned int    final_size;
   SubTableOffsets offsets;
 
+  unsigned int    orig_fdcount;
+  unsigned int    subst_fdcount;
+  inline bool     is_fds_subsetted (void) const { return subst_fdcount < orig_fdcount; }
+  unsigned int    subst_fdselect_format;
+  hb_vector_t<hb_codepoint_t>   subst_fdselect_first_glyphs;
+
+  /* font dict index remap table from fullset FDArray to subset FDArray.
+   * set to HB_SET_VALUE_INVALID if excluded from subset */
+  hb_vector_t<hb_codepoint_t>   fdmap;
+
   hb_vector_t<ByteStr> subset_charstrings;
   hb_vector_t<offset_size_pair> private_off_and_size_pairs;
 };
 
 static inline bool _write_cff2 (const subset_plan &plan,
                                 const OT::cff2::accelerator_subset_t  &acc,
+                                const hb_vector_t<hb_codepoint_t>& glyphs,
                                 unsigned int dest_sz,
                                 void *dest)
 {
@@ -286,14 +316,29 @@ static inline bool _write_cff2 (const subset_plan &plan,
   }
 
   /* FDSelect */
-  if (acc.fdSelect != &Null(FDSelect))
+  if (acc.fdSelect != &Null(CFF2FDSelect))
   {
     assert (plan.offsets.FDSelectOffset == c.head - c.start);
-    FDSelect *dest = c.start_embed<FDSelect> ();
-    if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+    
+    if (plan.is_fds_subsetted ())
     {
-      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDSelect");
-      return false;
+      if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *acc.fdSelect, acc.fdArray->count,
+                                                plan.subst_fdselect_format, plan.offsets.FDSelectSize,
+                                                plan.subst_fdselect_first_glyphs,
+                                                plan.fdmap)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
+        return false;
+      }
+    }
+    else
+    {
+      CFF2FDSelect *dest = c.start_embed<CFF2FDSelect> ();
+      if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDSelect");
+        return false;
+      }
     }
   }
 
@@ -303,7 +348,9 @@ static inline bool _write_cff2 (const subset_plan &plan,
     FDArray  *fda = c.start_embed<FDArray> ();
     if (unlikely (fda == nullptr)) return false;
     CFF2FontDict_OpSerializer  fontSzr;
-    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize, acc.fontDicts, fontSzr, plan.private_off_and_size_pairs)))
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize,
+                                   acc.fontDicts, plan.subst_fdcount, plan.fdmap,
+                                   fontSzr, plan.private_off_and_size_pairs)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
       return false;
@@ -373,7 +420,7 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t  &acc,
   unsigned int  cff2_prime_size = cff2_subset_plan.get_final_size ();
   char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
 
-  if (unlikely (!_write_cff2 (cff2_subset_plan, acc,
+  if (unlikely (!_write_cff2 (cff2_subset_plan, acc, plan->glyphs,
                               cff2_prime_size, cff2_prime_data))) {
     DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
     free (cff2_prime_data);