CFF1 no-desubroutinize + no-hinting
authorMichiharu Ariza <ariza@adobe.com>
Thu, 1 Nov 2018 05:30:34 +0000 (22:30 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Thu, 1 Nov 2018 05:30:34 +0000 (22:30 -0700)
no-desubroutinize option is disabled for now
code cleanup (esp. CFF1 width handling)
bug fixes & renaming

16 files changed:
src/hb-cff-interp-common.hh
src/hb-cff-interp-cs-common.hh
src/hb-cff-interp-dict-common.hh
src/hb-cff1-interp-cs.hh
src/hb-cff2-interp-cs.hh
src/hb-ot-cff-common.hh
src/hb-ot-cff1-table.hh
src/hb-ot-cff2-table.hh
src/hb-subset-cff-common.hh
src/hb-subset-cff1.cc
src/hb-subset-cff2.cc
src/hb-subset-input.hh
src/hb-subset-plan.cc
src/hb-subset-plan.hh
util/options.cc
util/options.hh

index fcf4d72..9567b6b 100644 (file)
@@ -448,6 +448,8 @@ struct SubByteStr
   bool          error;
 };
 
+typedef hb_vector_t<ByteStr> ByteStrArray;
+
 /* stack */
 template <typename ELEM, int LIMIT>
 struct Stack
@@ -652,6 +654,53 @@ struct OpSerializer
   }
 };
 
+template <typename VAL>
+struct ParsedValues
+{
+  inline void init (void)
+  {
+    opStart = 0;
+    values.init ();
+  }
+
+  inline void fini (void)
+  {
+    values.fini_deep ();
+  }
+
+  inline void add_op (OpCode op, const SubByteStr& substr = SubByteStr ())
+  {
+    VAL *val = values.push ();
+    val->op = op;
+    assert (substr.offset >= opStart);
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  inline void add_op (OpCode op, const SubByteStr& substr, const VAL &v)
+  {
+    VAL *val = values.push (v);
+    val->op = op;
+    assert (substr.offset >= opStart);
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  inline bool has_op (OpCode op) const
+  {
+    for (unsigned int i = 0; i < get_count (); i++)
+      if (get_value (i).op == op) return true;
+    return false;
+  }
+
+  inline unsigned get_count (void) const { return values.len; }
+  inline const VAL &get_value (unsigned int i) const { return values[i]; }
+  inline const VAL &operator [] (unsigned int i) const { return get_value (i); }
+
+  unsigned int       opStart;
+  hb_vector_t<VAL>   values;
+};
+
 template <typename ARG=Number>
 struct InterpEnv
 {
index f22d753..bddcb32 100644 (file)
@@ -33,8 +33,31 @@ namespace CFF {
 
 using namespace OT;
 
+enum CSType {
+  CSType_CharString,
+  CSType_GlobalSubr,
+  CSType_LocalSubr
+};
+
+struct CallContext
+{
+  inline void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
+  {
+    substr = substr_;
+    type = type_;
+    subr_num = subr_num_;
+  }
+
+  inline void fini (void) {}
+
+  SubByteStr      substr;
+  CSType          type;
+  unsigned int    subr_num;
+};
+
 /* call stack */
-struct CallStack : Stack<SubByteStr, 10> {};
+const unsigned int kMaxCallLimit = 10;
+struct CallStack : Stack<CallContext, kMaxCallLimit> {};
 
 template <typename SUBRS>
 struct BiasedSubrs
@@ -80,6 +103,7 @@ struct CSInterpEnv : InterpEnv<ARG>
   {
     InterpEnv<ARG>::init (str);
 
+    context.init (str, CSType_CharString);
     seen_moveto = true;
     seen_hintmask = false;
     hstem_count = 0;
@@ -114,23 +138,29 @@ struct CSInterpEnv : InterpEnv<ARG>
     return true;
   }
 
-  inline bool callSubr (const BiasedSubrs<SUBRS>& biasedSubrs)
+  inline void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
   {
     unsigned int subr_num;
 
-    if (unlikely (!popSubrNum (biasedSubrs, subr_num)))
-      return false;
-    callStack.push (SUPER::substr);
-    SUPER::substr = (*biasedSubrs.subrs)[subr_num];
+    if (unlikely (!popSubrNum (biasedSubrs, subr_num)
+                 || callStack.get_count () >= kMaxCallLimit))
+    {
+      SUPER::set_error ();
+      return;
+    }
+    context.substr = SUPER::substr;
+    callStack.push (context);
 
-    return true;
+    context.init ( (*biasedSubrs.subrs)[subr_num], type, subr_num);
+    SUPER::substr = context.substr;
   }
 
   inline void returnFromSubr (void)
   {
     if (unlikely (SUPER::substr.in_error ()))
       SUPER::set_error ();
-    SUPER::substr = callStack.pop ();
+    context = callStack.pop ();
+    SUPER::substr = context.substr;
   }
 
   inline void determine_hintmask_size (void)
@@ -153,6 +183,7 @@ struct CSInterpEnv : InterpEnv<ARG>
   inline void moveto (const Point &pt_ ) { pt = pt_; }
 
   public:
+  CallContext   context;
   bool          endchar_flag;
   bool          seen_moveto;
   bool          seen_hintmask;
@@ -206,6 +237,7 @@ struct CSOpSet : OpSet<ARG>
         env.returnFromSubr ();
         break;
       case OpCode_endchar:
+        OPSET::check_width (op, env, param);
         env.set_endchar (true);
         OPSET::flush_args_and_op (op, env, param);
         break;
@@ -215,36 +247,42 @@ struct CSOpSet : OpSet<ARG>
         break;
 
       case OpCode_callsubr:
-        env.callSubr (env.localSubrs);
+        env.callSubr (env.localSubrs, CSType_LocalSubr);
         break;
 
       case OpCode_callgsubr:
-        env.callSubr (env.globalSubrs);
+        env.callSubr (env.globalSubrs, CSType_GlobalSubr);
         break;
 
       case OpCode_hstem:
       case OpCode_hstemhm:
+        OPSET::check_width (op, env, param);
         OPSET::process_hstem (op, env, param);
         break;
       case OpCode_vstem:
       case OpCode_vstemhm:
+        OPSET::check_width (op, env, param);
         OPSET::process_vstem (op, env, param);
         break;
       case OpCode_hintmask:
       case OpCode_cntrmask:
+        OPSET::check_width (op, env, param);
         OPSET::process_hintmask (op, env, param);
         break;
       case OpCode_rmoveto:
+        OPSET::check_width (op, env, param);
         PATH::rmoveto (env, param);
-        process_post_move (op, env, param);
+        OPSET::process_post_move (op, env, param);
         break;
       case OpCode_hmoveto:
+        OPSET::check_width (op, env, param);
         PATH::hmoveto (env, param);
-        process_post_move (op, env, param);
+        OPSET::process_post_move (op, env, param);
         break;
       case OpCode_vmoveto:
+        OPSET::check_width (op, env, param);
         PATH::vmoveto (env, param);
-        process_post_move (op, env, param);
+        OPSET::process_post_move (op, env, param);
         break;
       case OpCode_rlineto:
         PATH::rlineto (env, param);
@@ -340,6 +378,9 @@ struct CSOpSet : OpSet<ARG>
     OPSET::flush_args_and_op (op, env, param);
   }
 
+  static inline void check_width (OpCode op, ENV &env, PARAM& param)
+  {}
+
   static inline void process_post_move (OpCode op, ENV &env, PARAM& param)
   {
     if (!env.seen_moveto)
@@ -355,15 +396,15 @@ struct CSOpSet : OpSet<ARG>
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param, unsigned int start_arg = 0)
+  static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
   {
-    OPSET::flush_args (env, param, start_arg);
+    OPSET::flush_args (env, param);
     OPSET::flush_op (op, env, param);
   }
 
-  static inline void flush_args (ENV &env, PARAM& param, unsigned int start_arg = 0)
+  static inline void flush_args (ENV &env, PARAM& param)
   {
-    env.pop_n_args (env.argStack.get_count () - start_arg);
+    env.pop_n_args (env.argStack.get_count ());
   }
 
   static inline void flush_op (OpCode op, ENV &env, PARAM& param)
@@ -375,6 +416,24 @@ struct CSOpSet : OpSet<ARG>
     OPSET::flush_args_and_op (op, env, param);
   }
 
+  static inline bool is_number_op (OpCode op)
+  {
+    switch (op)
+    {
+      case OpCode_shortint:
+      case OpCode_fixedcs:
+      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+        return true;
+
+      default:
+        /* 1-byte integer */
+        return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
+    }
+  }
+
   protected:
   typedef OpSet<ARG>  SUPER;
 };
index 56988a3..88fd345 100644 (file)
@@ -50,50 +50,7 @@ struct DictVal : OpStr
 
 typedef DictVal NumDictVal;
 
-template <typename VAL>
-struct DictValues
-{
-  inline void init (void)
-  {
-    opStart = 0;
-    values.init ();
-  }
-
-  inline void fini (void)
-  {
-    values.fini_deep ();
-  }
-
-  inline void addOp (OpCode op, const SubByteStr& substr = SubByteStr ())
-  {
-    VAL *val = values.push ();
-    val->op = op;
-    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
-    opStart = substr.offset;
-  }
-
-  inline void addOp (OpCode op, const SubByteStr& substr, const VAL &v)
-  {
-    VAL *val = values.push (v);
-    val->op = op;
-    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
-    opStart = substr.offset;
-  }
-
-  inline bool hasOp (OpCode op) const
-  {
-    for (unsigned int i = 0; i < getNumValues (); i++)
-      if (getValue (i).op == op) return true;
-    return false;
-  }
-
-  inline unsigned getNumValues (void) const { return values.len; }
-  inline const VAL &getValue (unsigned int i) const { return values[i]; }
-  inline const VAL &operator [] (unsigned int i) const { return getValue (i); }
-
-  unsigned int       opStart;
-  hb_vector_t<VAL>   values;
-};
+template <typename VAL> struct DictValues : ParsedValues<VAL> {};
 
 template <typename OPSTR=OpStr>
 struct TopDictValues : DictValues<OPSTR>
index d011461..8a10abc 100644 (file)
@@ -33,6 +33,8 @@ namespace CFF {
 
 using namespace OT;
 
+typedef BiasedSubrs<CFF1Subrs>   CFF1BiasedSubrs;
+
 struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
 {
   template <typename ACC>
@@ -41,6 +43,7 @@ struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
     SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
     processed_width = false;
     has_width = false;
+    arg_start = 0;
   }
 
   inline void fini (void)
@@ -48,24 +51,26 @@ struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
     SUPER::fini ();
   }
 
-  inline unsigned int check_width (void)
+  inline void set_width (void)
   {
-    unsigned int arg_start = 0;
-    if (!processed_width)
+    if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
     {
-      if ((SUPER::argStack.get_count () & 1) != 0)
-      {
-        width = SUPER::argStack[0];
-        has_width = true;
-        arg_start = 1;
-      }
+      width = SUPER::argStack[0];
+      has_width = true;
       processed_width = true;
+      arg_start = 1;
     }
-    return arg_start;
+  }
+
+  inline void clear_args (void)
+  {
+    arg_start = 0;
+    SUPER::clear_args ();
   }
 
   bool          processed_width;
   bool          has_width;
+  unsigned int  arg_start;
   Number        width;
 
   private:
@@ -77,10 +82,45 @@ struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
 {
   /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
 
-  static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param, unsigned int start_arg = 0)
+  static inline void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  {
+    if (!env.processed_width)
+    {
+      bool  has_width = false;
+      switch (op)
+      {
+        default:
+        case OpCode_endchar:
+          has_width = (env.argStack.get_count () > 0);
+          break;
+        case OpCode_hstem:
+        case OpCode_hstemhm:
+        case OpCode_hintmask:
+        case OpCode_cntrmask:
+          has_width = ((env.argStack.get_count () & 1) != 0);
+          break;
+        case OpCode_hmoveto:
+        case OpCode_vmoveto:
+          has_width = (env.argStack.get_count () > 1);
+          break;
+        case OpCode_rmoveto:
+          has_width = (env.argStack.get_count () > 2);
+          break;
+      }
+      if (has_width)
+      {
+        env.set_width ();
+        OPSET::process_width (env, param);
+      }
+    }
+  }
+
+  static inline void process_width (CFF1CSInterpEnv &env, PARAM& param)
+  {}
+
+  static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param)
   {
-    start_arg = env.check_width ();
-    SUPER::flush_args (env, param, start_arg);
+    SUPER::flush_args (env, param);
     env.clear_args ();  /* pop off width */
   }
 
index 71f2b40..1887746 100644 (file)
@@ -74,6 +74,7 @@ struct BlendArg : Number
 };
 
 typedef InterpEnv<BlendArg> BlendInterpEnv;
+typedef BiasedSubrs<CFF2Subrs>   CFF2BiasedSubrs;
 
 struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
 {
index a7ff197..40ae957 100644 (file)
@@ -61,6 +61,18 @@ struct code_pair
   hb_codepoint_t  glyph;
 };
 
+typedef hb_vector_t<char, 1> StrBuff;
+struct StrBuffArray :  hb_vector_t<StrBuff>
+{
+  inline unsigned int total_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < len; i++)
+      size += (*this)[i].len;
+    return size;
+  }
+};
+
 /* CFF INDEX */
 template <typename COUNT>
 struct CFFIndex
@@ -95,7 +107,7 @@ struct CFFIndex
 
   inline bool serialize (hb_serialize_context_t *c,
                          unsigned int offSize_,
-                         const hb_vector_t<ByteStr> &byteArray)
+                         const ByteStrArray &byteArray)
   {
     TRACE_SERIALIZE (this);
     /* serialize CFFIndex header */
@@ -126,6 +138,22 @@ struct CFFIndex
     return_trace (true);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+                         unsigned int offSize_,
+                         const StrBuffArray &buffArray)
+  {
+    ByteStrArray  byteArray;
+    byteArray.init ();
+    byteArray.resize (buffArray.len);
+    for (unsigned int i = 0; i < byteArray.len; i++)
+    {
+      byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len);
+    }
+    bool result = this->serialize (c, offSize_, byteArray);
+    byteArray.fini ();
+    return result;
+  }
+
   inline void set_offset_at (unsigned int index, unsigned int offset)
   {
     HBUINT8 *p = offsets + offSize * index + offSize;
@@ -280,7 +308,7 @@ struct Dict : UnsizedByteStr
                         PARAM& param)
   {
     TRACE_SERIALIZE (this);
-    for (unsigned int i = 0; i < dictval.getNumValues (); i++)
+    for (unsigned int i = 0; i < dictval.get_count (); i++)
     {
       if (unlikely (!opszr.serialize (c, dictval[i], param)))
         return_trace (false);
@@ -294,7 +322,7 @@ struct Dict : UnsizedByteStr
                                                         OP_SERIALIZER& opszr)
   {
     unsigned int size = 0;
-    for (unsigned int i = 0; i < dictval.getNumValues (); i++)
+    for (unsigned int i = 0; i < dictval.get_count (); i++)
       size += opszr.calculate_serialized_size (dictval[i]);
     return size;
   }
@@ -383,6 +411,9 @@ struct Remap : hb_vector_t<hb_codepoint_t>
   inline bool excludes (hb_codepoint_t id) const
   { return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
 
+  inline bool includes (hb_codepoint_t id) const
+  { return !excludes (id); }
+
   inline hb_codepoint_t operator[] (hb_codepoint_t i) const
   {
     if (fullset ())
@@ -649,52 +680,8 @@ struct FDSelect {
 template <typename COUNT>
 struct Subrs : CFFIndex<COUNT>
 {
-  inline bool serialize (hb_serialize_context_t *c, const Subrs<COUNT> &subrs, unsigned int offSize, const hb_set_t *set, const ByteStr& nullStr = ByteStr())
-  {
-    TRACE_SERIALIZE (this);
-    if (&subrs == &Null(Subrs<COUNT>))
-      return_trace (true);
-    if ((subrs.count == 0) || (set == nullptr) || (hb_set_is_empty (set)))
-    {
-      if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
-        return_trace (false);
-      CFFIndex<COUNT>::count.set (0);
-      return_trace (true);
-    }
-    
-    hb_vector_t<ByteStr> bytesArray;
-    bytesArray.init ();
-    if (!bytesArray.resize (subrs.count))
-      return_trace (false);
-    for (hb_codepoint_t i = 0; i < subrs.count; i++)
-      bytesArray[i] = (hb_set_has (set, i))? subrs[i]: nullStr;
-
-    bool result = CFFIndex<COUNT>::serialize (c, offSize, bytesArray);
-    bytesArray.fini ();
-    return_trace (result);
-  }
-  
-  /* in parallel to above */
-  inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_set_t *set, unsigned int nullStrSize = 0) const
-  {
-    if (this == &Null(Subrs<COUNT>))
-      return 0;
-    unsigned int  count_ = CFFIndex<COUNT>::count;
-    offSize = 0;
-    if ((count_ == 0) || (hb_set_get_population (set) == 0))
-      return COUNT::static_size;
-
-    unsigned int dataSize = 0;
-    for (hb_codepoint_t i = 0; i < count_; i++)
-    {
-      if (hb_set_has (set, i))
-        dataSize += (*this)[i].len;
-      else
-        dataSize += nullStrSize;
-    }
-    offSize = calcOffSize(dataSize);
-    return CFFIndex<COUNT>::calculate_serialized_size (offSize, count_, dataSize);
-  }
+  typedef COUNT count_type;
+  typedef CFFIndex<COUNT> SUPER;
 };
 
 } /* namespace CFF */
index b99b8cf..bc67e49 100644 (file)
@@ -537,7 +537,7 @@ struct CFF1StringIndex : CFF1Index
       return_trace (true);
     }
     
-    hb_vector_t<ByteStr> bytesArray;
+    ByteStrArray bytesArray;
     bytesArray.init ();
     if (!bytesArray.resize (sidmap.get_count ()))
       return_trace (false);
@@ -757,7 +757,7 @@ struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal>
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr, val);
+    dictval.add_op (op, env.substr, val);
   }
 };
 
@@ -806,7 +806,7 @@ struct CFF1FontDictOpSet : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr);
+    dictval.add_op (op, env.substr);
   }
 };
 
@@ -828,11 +828,11 @@ struct CFF1PrivateDictValues_Base : DictValues<VAL>
   inline unsigned int calculate_serialized_size (void) const
   {
     unsigned int size = 0;
-    for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
-      if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
+    for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
+      if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
         size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
       else
-        size += DictValues<VAL>::getValue (i).str.len;
+        size += DictValues<VAL>::get_value (i).str.len;
     return size;
   }
 
@@ -886,7 +886,7 @@ struct CFF1PrivateDictOpSet : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr, val);
+    dictval.add_op (op, env.substr, val);
   }
 };
 
@@ -928,7 +928,7 @@ struct CFF1PrivateDictOpSet_Subset : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr);
+    dictval.add_op (op, env.substr);
   }
 };
 
@@ -989,6 +989,7 @@ struct cff1
         if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
         CFF1TopDict_Interpreter top_interp;
         top_interp.env.init (topDictStr);
+        topDict.init ();
         if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
       }
       
@@ -1041,12 +1042,14 @@ struct cff1
           CFF1FontDict_Interpreter font_interp;
           font_interp.env.init (fontDictStr);
           font = fontDicts.push ();
+          font->init ();
           if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
           PRIVDICTVAL  *priv = &privateDicts[i];
           const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
           if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
           DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
           priv_interp.env.init (privDictStr);
+          priv->init ();
           if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
           priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
@@ -1064,6 +1067,7 @@ struct cff1
         if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
         DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
         priv_interp.env.init (privDictStr);
+        priv->init ();
         if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
 
         priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
index e27ace0..a12ef56 100644 (file)
@@ -153,9 +153,9 @@ struct CFF2TopDictValues : TopDictValues<>
   inline unsigned int calculate_serialized_size (void) const
   {
     unsigned int size = 0;
-    for (unsigned int i = 0; i < getNumValues (); i++)
+    for (unsigned int i = 0; i < get_count (); i++)
     {
-      OpCode op = getValue (i).op;
+      OpCode op = get_value (i).op;
       switch (op)
       {
         case OpCode_vstore:
@@ -163,7 +163,7 @@ struct CFF2TopDictValues : TopDictValues<>
           size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
           break;
         default:
-          size += TopDictValues<>::calculate_serialized_op_size (getValue (i));
+          size += TopDictValues<>::calculate_serialized_op_size (get_value (i));
           break;
       }
     }
@@ -183,7 +183,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
         {
           DictVal val;
           val.init ();
-          dictval.addOp (op, env.substr);
+          dictval.add_op (op, env.substr);
           env.clear_args ();
         }
         break;
@@ -205,7 +205,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr);
+    dictval.add_op (op, env.substr);
   }
 
   typedef TopDictOpSet<> SUPER;
@@ -246,7 +246,7 @@ struct CFF2FontDictOpSet : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr);
+    dictval.add_op (op, env.substr);
   }
 
   private:
@@ -272,11 +272,11 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
   inline unsigned int calculate_serialized_size (void) const
   {
     unsigned int size = 0;
-    for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
-      if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
+    for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
+      if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
         size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
       else
-        size += DictValues<VAL>::getValue (i).str.len;
+        size += DictValues<VAL>::get_value (i).str.len;
     return size;
   }
 
@@ -359,7 +359,7 @@ struct CFF2PrivateDictOpSet : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr, val);
+    dictval.add_op (op, env.substr, val);
   }
 };
 
@@ -401,7 +401,7 @@ struct CFF2PrivateDictOpSet_Subset : DictOpSet
 
     if (unlikely (env.in_error ())) return;
 
-    dictval.addOp (op, env.substr);
+    dictval.add_op (op, env.substr);
   }
 
   private:
@@ -453,6 +453,7 @@ struct cff2
         if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
         CFF2TopDict_Interpreter top_interp;
         top_interp.env.init (topDictStr);
+        topDict.init ();
         if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
       }
       
@@ -484,12 +485,14 @@ struct cff2
         CFF2FontDict_Interpreter font_interp;
         font_interp.env.init (fontDictStr);
         font = fontDicts.push ();
+        font->init ();
         if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
 
         const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
         if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
         DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv>  priv_interp;
         priv_interp.env.init(privDictStr);
+        privateDicts[i].init ();
         if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
 
         privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
index 5494364..6debb85 100644 (file)
 namespace CFF {
 
 /* Used for writing a temporary charstring */
-struct ByteStrBuff : hb_vector_t<char, 1>
+struct StrEncoder
 {
-  inline bool encode_byte (unsigned char b)
+  inline StrEncoder (StrBuff &buff_)
+    : buff (buff_), error (false)
+  {}
+
+  inline void reset (void)
+  {
+    buff.resize (0);
+  }
+
+  inline void encode_byte (unsigned char b)
   {
-    return (push ((const char)b) != &Crap(char));
+    if (unlikely (buff.push ((const char)b) == &Crap(char)))
+      set_error ();
   }
 
-  inline bool encode_int (int v)
+  inline void encode_int (int v)
   {
     if ((-1131 <= v) && (v <= 1131))
     {
       if ((-107 <= v) && (v <= 107))
-        return encode_byte (v + 139);
+        encode_byte (v + 139);
       else if (v > 0)
       {
         v -= 108;
-        return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF);
+        encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
+        encode_byte (v & 0xFF);
       }
       else
       {
         v = -v - 108;
-        return encode_byte ((v >> 8) + OpCode_TwoByteNegInt0) && encode_byte (v & 0xFF);
+        encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
+        encode_byte (v & 0xFF);
       }
     }
-    if (unlikely (v < -32768))
-      v = -32768;
-    else if (unlikely (v > 32767))
-      v = 32767;
-    return encode_byte (OpCode_shortint) &&
-           encode_byte ((v >> 8) & 0xFF) &&
-           encode_byte (v & 0xFF);
+    else
+    {
+      if (unlikely (v < -32768))
+        v = -32768;
+      else if (unlikely (v > 32767))
+        v = 32767;
+      encode_byte (OpCode_shortint);
+      encode_byte ((v >> 8) & 0xFF);
+      encode_byte (v & 0xFF);
+    }
   }
 
-  inline bool encode_num (const Number& n)
+  inline void encode_num (const Number& n)
   {
     if (n.in_int_range ())
     {
-      return encode_int (n.to_int ());
+      encode_int (n.to_int ());
     }
     else
     {
       int32_t v = n.to_fixed ();
-      return encode_byte (OpCode_fixedcs) &&
-             encode_byte ((v >> 24) & 0xFF) &&
-             encode_byte ((v >> 16) & 0xFF) &&
-             encode_byte ((v >> 8) & 0xFF) &&
-             encode_byte (v & 0xFF);
+      encode_byte (OpCode_fixedcs);
+      encode_byte ((v >> 24) & 0xFF);
+      encode_byte ((v >> 16) & 0xFF);
+      encode_byte ((v >> 8) & 0xFF);
+      encode_byte (v & 0xFF);
     }
   }
 
-  inline bool encode_op (OpCode op)
+  inline void encode_op (OpCode op)
   {
     if (Is_OpCode_ESC (op))
-      return encode_byte (OpCode_escape) &&
-             encode_byte (Unmake_OpCode_ESC (op));
+    {
+      encode_byte (OpCode_escape);
+      encode_byte (Unmake_OpCode_ESC (op));
+    }
     else
-      return encode_byte (op);
+      encode_byte (op);
   }
-};
 
-struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1>
-{
-  inline void fini (void)
+  inline void copy_str (const ByteStr &str)
   {
-    for (unsigned int i = 0; i < len; i++)
-      hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini ();
-    hb_vector_t<ByteStrBuff, 1>::fini ();
+    unsigned int  offset = buff.len;
+    buff.resize (offset + str.len);
+    if (unlikely (buff.len < offset + str.len))
+    {
+      set_error ();
+      return;
+    }
+    memcpy (&buff[offset], &str.str[0], str.len);
   }
+
+  inline bool is_error (void) const { return error; }
+
+  protected:
+  inline void set_error (void) { error = true; }
+  
+  StrBuff &buff;
+  bool    error;
 };
 
 struct CFFSubTableOffsets {
@@ -215,8 +242,8 @@ struct CFFFontDict_OpSerializer : OpSerializer
 
 struct CFFPrivateDict_OpSerializer : OpSerializer
 {
-  inline CFFPrivateDict_OpSerializer (bool drop_hints_=false)
-    : drop_hints (drop_hints_) {}
+  inline CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
+    : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
 
   inline bool serialize (hb_serialize_context_t *c,
                          const OpStr &opstr,
@@ -227,7 +254,12 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
     if (drop_hints && DictOpSet::is_hint_op (opstr.op))
       return true;
     if (opstr.op == OpCode_Subrs)
-      return_trace (true);
+    {
+      if (desubroutinize)
+        return_trace (true);
+      else
+        return_trace (FontDict::serialize_offset4_op (c, opstr.op, subrsOffset));
+    }
     else
       return_trace (copy_opstr (c, opstr));
   }
@@ -237,18 +269,24 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
     if (drop_hints && DictOpSet::is_hint_op (opstr.op))
       return 0;
     if (opstr.op == OpCode_Subrs)
-      return 0;
+    {
+      if (desubroutinize)
+        return 0;
+      else
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+    }
     else
       return opstr.str.len;
   }
 
   protected:
+  const bool  desubroutinize;
   const bool  drop_hints;
 };
 
 struct FlattenParam
 {
-  ByteStrBuff &flatStr;
+  StrBuff     &flatStr;
   bool        drop_hints;
 };
 
@@ -263,7 +301,7 @@ struct SubrFlattener
       drop_hints (drop_hints_)
   {}
 
-  inline bool flatten (ByteStrBuffArray &flat_charstrings)
+  inline bool flatten (StrBuffArray &flat_charstrings)
   {
     if (!flat_charstrings.resize (glyphs.len))
       return false;
@@ -287,6 +325,594 @@ struct SubrFlattener
   const hb_vector_t<hb_codepoint_t> &glyphs;
   bool  drop_hints;
 };
+
+struct SubrClosures
+{
+  inline SubrClosures (void)
+    : valid (false),
+      global_closure (nullptr)
+  {
+    local_closures.init ();
+  }
+
+  inline void init (unsigned int fd_count)
+  {
+    valid = true;
+    global_closure = hb_set_create ();
+    if (global_closure == hb_set_get_empty ())
+      valid = false;
+    if (!local_closures.resize (fd_count))
+      valid = false;
+
+    for (unsigned int i = 0; i < local_closures.len; i++)
+    {
+      local_closures[i] = hb_set_create ();
+      if (local_closures[i] == hb_set_get_empty ())
+        valid = false;
+    }
+  }
+
+  inline void fini (void)
+  {
+    hb_set_destroy (global_closure);
+    for (unsigned int i = 0; i < local_closures.len; i++)
+      hb_set_destroy (local_closures[i]);
+    local_closures.fini ();
+  }
+
+  inline void reset (void)
+  {
+    hb_set_clear (global_closure);
+    for (unsigned int i = 0; i < local_closures.len; i++)
+      hb_set_clear (local_closures[i]);
+  }
+
+  bool is_valid (void) const { return valid; }
+  bool  valid;
+  hb_set_t  *global_closure;
+  hb_vector_t<hb_set_t *> local_closures;
+};
+
+struct ParsedCSOp : OpStr
+{
+  inline void init (unsigned int subr_num_ = 0)
+  {
+    OpStr::init ();
+    flags = kDropFlag_None;
+    subr_num = subr_num_;
+  }
+
+  inline void fini (void)
+  {
+    OpStr::fini ();
+  }
+
+  inline bool for_keep (void) const { return (flags & kDropFlag_Keep) != 0; }
+  inline bool for_drop (void) const { return (flags & kDropFlag_Drop) != 0; }
+  inline void set_drop (void) { if (!for_keep ()) flags |= kDropFlag_Drop; }
+  inline void set_keep (void) { flags |= kDropFlag_Keep; }
+
+  enum DropFlag
+  {
+    kDropFlag_None  = 0,
+    kDropFlag_Drop  = 1,
+    kDropFlag_Keep  = 2
+  };
+
+  unsigned int  flags;
+  unsigned int  subr_num;
+};
+
+struct ParsedCStr : ParsedValues<ParsedCSOp>
+{
+  inline void init (void)
+  {
+    SUPER::init ();
+    parsed = false;
+    hint_removed = false;
+    has_prefix_ = false;
+  }
+
+  inline void add_op (OpCode op, const SubByteStr& substr)
+  {
+    if (!is_parsed ())
+      SUPER::add_op (op, substr);
+  }
+
+  inline void addCallOp (OpCode op, const SubByteStr& substr, unsigned int subr_num)
+  {
+    if (!is_parsed ())
+    {
+      unsigned int parsed_len = get_count ();
+      if (likely (parsed_len > 0))
+        values[parsed_len-1].set_drop ();
+      
+      ParsedCSOp val;
+      val.init (subr_num);
+      SUPER::add_op (op, substr, val);
+    }
+  }
+
+  inline void set_prefix (const Number &num, OpCode op = OpCode_Invalid)
+  {
+    has_prefix_ = true;
+    prefix_op_ = op;
+    prefix_num_ = num;
+  }
+
+  inline bool is_parsed (void) const { return parsed; }
+  inline void set_parsed (void) { parsed = true; }
+  inline bool is_hint_removed (void) const { return hint_removed; }
+  inline void set_hint_removed (void) { hint_removed = true; }
+  inline bool has_prefix (void) const { return has_prefix_; }
+  inline OpCode prefix_op (void) const { return prefix_op_; }
+  inline const Number &prefix_num (void) const { return prefix_num_; }
+
+  protected:
+  bool    parsed;
+  bool    hint_removed;
+  bool    has_prefix_;
+  OpCode  prefix_op_;
+  Number  prefix_num_;
+
+  private:
+  typedef ParsedValues<ParsedCSOp> SUPER;
+};
+
+struct ParsedCStrs : hb_vector_t<ParsedCStr>
+{
+  inline void init (unsigned int len_ = 0)
+  {
+    hb_vector_t<ParsedCStr>::init ();
+    resize (len_);
+    for (unsigned int i = 0; i < len; i++)
+      (*this)[i].init ();
+  }
+};
+
+struct SubrSubsetParam
+{
+  inline void init (ParsedCStr *parsed_charstring_,
+                    ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_,
+                    hb_set_t *global_closure_, hb_set_t *local_closure_,
+                    bool drop_hints_)
+  {
+    parsed_charstring = parsed_charstring_;
+    current_parsed_str = parsed_charstring;
+    parsed_global_subrs = parsed_global_subrs_;
+    parsed_local_subrs = parsed_local_subrs_;
+    global_closure = global_closure_;
+    local_closure = local_closure_;
+    drop_hints = drop_hints_;
+  }
+
+  template <typename ENV>
+  inline void set_current_str (ENV &env)
+  {
+    const CallContext &context = env.context;
+  
+    switch (context.type)
+    {
+      case CSType_CharString:
+        current_parsed_str = parsed_charstring;
+        break;
+      
+      case CSType_LocalSubr:
+        if (likely (context.subr_num < parsed_local_subrs->len))
+          current_parsed_str = &(*parsed_local_subrs)[context.subr_num];
+        else
+          env.set_error ();
+        break;
+
+      case CSType_GlobalSubr:
+        if (likely (context.subr_num < parsed_global_subrs->len))
+          current_parsed_str = &(*parsed_global_subrs)[context.subr_num];
+        else
+          env.set_error ();
+        break;
+
+      default:
+        assert (0);
+    }
+  }
+
+  ParsedCStr    *current_parsed_str;
+
+  ParsedCStr    *parsed_charstring;
+  ParsedCStrs   *parsed_global_subrs;
+  ParsedCStrs   *parsed_local_subrs;
+  hb_set_t      *global_closure;
+  hb_set_t      *local_closure;
+  bool          drop_hints;
+};
+
+struct SubrRemap : Remap
+{
+  inline void create (hb_set_t *closure)
+  {
+    /* create a remapping of subroutine numbers from old to new.
+     * no optimization based on usage counts. fonttools doesn't appear doing that either.
+     */
+    reset (closure->get_max () + 1);
+    for (hb_codepoint_t old_num = 0; old_num < len; old_num++)
+    {
+      if (hb_set_has (closure, old_num))
+        add (old_num);
+    }
+  
+    if (get_count () < 1240)
+      bias = 107;
+    else if (get_count () < 33900)
+      bias = 1131;
+    else
+      bias = 32768;
+  }
+
+  inline hb_codepoint_t operator[] (unsigned int old_num) const
+  {
+    if (old_num >= len)
+      return CFF_UNDEF_CODE;
+    else
+      return Remap::operator[] (old_num);
+  }
+
+  inline int biased_num (unsigned int old_num) const
+  {
+    return (int)(*this)[old_num] - bias;
+  }
+
+  protected:
+  int bias;
+};
+
+struct SubrRemaps
+{
+  inline SubrRemaps (void)
+  {
+    global_remap.init ();
+    local_remaps.init ();
+  }
+
+  inline ~SubrRemaps (void)
+  {
+    fini ();
+  }
+
+  inline void init (unsigned int fdCount)
+  {
+    local_remaps.resize (fdCount);
+    for (unsigned int i = 0; i < fdCount; i++)
+      local_remaps[i].init ();
+  }
+
+  inline void create (SubrClosures& closures)
+  {
+    global_remap.create (closures.global_closure);
+    for (unsigned int i = 0; i < local_remaps.len; i++)
+      local_remaps[i].create (closures.local_closures[i]);
+  }
+
+  inline void fini (void)
+  {
+    global_remap.fini ();
+    local_remaps.fini_deep ();
+  }
+
+  SubrRemap               global_remap;
+  hb_vector_t<SubrRemap>  local_remaps;
+};
+
+template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET>
+struct SubrSubsetter
+{
+  inline SubrSubsetter (void)
+  {
+    parsed_charstrings.init ();
+    parsed_global_subrs.init ();
+    parsed_local_subrs.init ();
+  }
+
+  inline ~SubrSubsetter (void)
+  {
+    closures.fini ();
+    remaps.fini ();
+    parsed_charstrings.fini_deep ();
+    parsed_global_subrs.fini_deep ();
+    parsed_local_subrs.fini_deep ();
+  }
+
+  /* Subroutine subsetting with --no-desubroutinize runs in phases:
+   *
+   * 1. execute charstrings/subroutines to determine subroutine closures
+   * 2. parse out all operators and numbers
+   * 3. mark hint operators and operands for removal if --no-hinting
+   * 4. re-encode all charstrings and subroutines with new subroutine numbers
+   *
+   * Phases #1 and #2 are done at the same time in collect_subrs ().
+   * Phase #3 requires walking charstrings/subroutines forward then backward (hence parsing), because
+   * we can't tell if a number belongs to a hint op until we see the first moveto.
+   *
+   * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
+   * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
+   */
+  inline bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints)
+  {
+    closures.init (acc.fdCount);
+    remaps.init (acc.fdCount);
+
+    parsed_charstrings.init (glyphs.len);
+    parsed_global_subrs.init (acc.globalSubrs->count);
+    parsed_local_subrs.resize (acc.fdCount);
+    for (unsigned int i = 0; i < acc.fdCount; i++)
+    {
+      parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
+    }
+    if (unlikely (!closures.valid))
+      return false;
+
+    /* phase 1 & 2 */
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      hb_codepoint_t  glyph = glyphs[i];
+      const ByteStr str = (*acc.charStrings)[glyph];
+      unsigned int fd = acc.fdSelect->get_fd (glyph);
+      
+      CSInterpreter<ENV, OPSET, SubrSubsetParam> interp;
+      interp.env.init (str, acc, fd);
+
+      SubrSubsetParam  param;
+      param.init (&parsed_charstrings[i],
+                  &parsed_global_subrs,  &parsed_local_subrs[fd],
+                  closures.global_closure, closures.local_closures[fd],
+                  drop_hints);
+
+      if (unlikely (!interp.interpret (param)))
+        return false;
+
+      /* copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
+      SUBSETTER::set_parsed_prefix (interp.env, parsed_charstrings[i]);
+    }
+    
+    if (drop_hints)
+    {
+      /* mark hint ops and arguments for drop */
+      for (unsigned int i = 0; i < glyphs.len; i++)
+      {
+        unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
+        SubrSubsetParam  param;
+        param.init (&parsed_charstrings[i],
+                    &parsed_global_subrs,  &parsed_local_subrs[fd],
+                    closures.global_closure, closures.local_closures[fd],
+                    drop_hints);
+
+        bool seen_moveto = false;
+        if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto))
+          parsed_charstrings[i].set_hint_removed ();
+      }
+
+      /* after dropping hints recreate closures from subrs actually used */
+      closures.reset ();
+      for (unsigned int i = 0; i < glyphs.len; i++)
+      {
+        unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
+        SubrSubsetParam  param;
+        param.init (&parsed_charstrings[i],
+                    &parsed_global_subrs,  &parsed_local_subrs[fd],
+                    closures.global_closure, closures.local_closures[fd],
+                    drop_hints);
+        collect_subr_refs_in_str (parsed_charstrings[i], param);
+      }
+    }
+    
+    remaps.create (closures);
+    
+    return true;
+  }
+
+  inline bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const
+  {
+    if (unlikely (!buffArray.resize (glyphs.len)))
+      return false;
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      unsigned int  fd = acc.fdSelect->get_fd (glyphs[i]);
+      if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
+        return false;
+    }
+    return true;
+  }
+
+  inline bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, StrBuffArray &buffArray) const
+  {
+    unsigned int  count = remap.get_count ();
+  
+    if (unlikely (!buffArray.resize (count)))
+      return false;
+    for (unsigned int old_num = 0; old_num < subrs.len; old_num++)
+    {
+      hb_codepoint_t new_num = remap[old_num];
+      if (new_num != CFF_UNDEF_CODE)
+      {
+        if (unlikely (!encode_str (subrs[old_num], 0, buffArray[new_num])))
+          return false;
+      }
+    }
+    return true;
+  }
+
+  inline bool encode_globalsubrs (StrBuffArray &buffArray)
+  {
+    return encode_subrs (parsed_global_subrs, remaps.global_remap, buffArray);
+  }
+
+  inline bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const
+  {
+    return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], buffArray);
+  }
+
+  protected:
+  inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
+                                 ParsedCStrs &subrs, unsigned int subr_num,
+                                 const SubrSubsetParam &param, bool &seen_moveto)
+  {
+    if (drop_hints_in_str (subrs[subr_num], param, seen_moveto))
+    {
+      /* if the first op in the subr is a hint op, then all args/ops (especially including other subr calls)
+       * preceding this subr no and call op are hints */
+      /* TODO CFF2 vsindex */
+      for (unsigned int i = 0; i + 1 < pos; i++)
+        str.values[i].set_drop ();
+      return true;
+    }
+    else
+      return false;
+  }
+
+  /* returns true if it sees a hint op before moveto */
+  inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, bool &seen_moveto)
+  {
+    bool  seen_hint = false;
+    unsigned int next_check_pos = 0;
+
+    for (unsigned int pos = 0; pos < str.values.len; pos++)
+    {
+      switch (str.values[pos].op)
+      {
+        case OpCode_callsubr:
+          seen_hint |= drop_hints_in_subr (str, pos,
+                                           *param.parsed_local_subrs, str.values[pos].subr_num,
+                                           param, seen_moveto);
+          break;
+
+        case OpCode_callgsubr:
+          seen_hint |= drop_hints_in_subr (str, pos,
+                                           *param.parsed_global_subrs, str.values[pos].subr_num,
+                                           param, seen_moveto);
+          break;
+
+        case OpCode_rmoveto:
+        case OpCode_hmoveto:
+        case OpCode_vmoveto:
+          seen_moveto = true;
+          break;
+
+        case OpCode_hintmask:
+        case OpCode_cntrmask:
+          if (seen_moveto)
+          {
+            str.values[pos].set_drop ();
+            break;
+          }
+          HB_FALLTHROUGH;
+
+        case OpCode_hstemhm:
+        case OpCode_vstemhm:
+        case OpCode_hstem:
+        case OpCode_vstem:
+          seen_hint = true;
+          for (unsigned int i = next_check_pos; i <= pos; i++)
+          {
+            /* TODO: CFF2 vsindex */
+            str.values[i].set_drop ();
+          }
+          next_check_pos = pos + 1;
+          break;
+
+        default:
+          /* NONE */
+          break;
+      }
+    }
+
+    return seen_hint;
+  }
+
+  inline void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos,
+                                         unsigned int subr_num, ParsedCStrs &subrs,
+                                         hb_set_t *closure,
+                                         const SubrSubsetParam &param)
+  {
+    hb_set_add (closure, subr_num);
+    collect_subr_refs_in_str (subrs[subr_num], param);
+  }
+
+  inline void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam &param)
+  {
+    for (unsigned int pos = 0; pos < str.values.len; pos++)
+    {
+      if (!str.values[pos].for_drop ())
+      {
+        switch (str.values[pos].op)
+        {
+          case OpCode_callsubr:
+            collect_subr_refs_in_subr (str, pos,
+                                       str.values[pos].subr_num, *param.parsed_local_subrs,
+                                       param.local_closure, param);
+            break;
+
+          case OpCode_callgsubr:
+            collect_subr_refs_in_subr (str, pos,
+                                       str.values[pos].subr_num, *param.parsed_global_subrs,
+                                       param.global_closure, param);
+            break;
+
+          default: break;
+        }
+      }
+    }
+  }
+
+  inline bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const
+  {
+    buff.init ();
+    StrEncoder  encoder (buff);
+    encoder.reset ();
+    /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
+     * re-insert it at the beginning of charstreing */
+    if (str.has_prefix () && str.is_hint_removed ())
+    {
+      encoder.encode_num (str.prefix_num ());
+      if (str.prefix_op () != OpCode_Invalid)
+        encoder.encode_op (str.prefix_op ());
+    }
+    for (unsigned int i = 0; i < str.get_count(); i++)
+    {
+      const ParsedCSOp  &opstr = str.values[i];
+      if (!opstr.for_drop ())
+      {
+        switch (opstr.op)
+        {
+          case OpCode_callsubr:
+            encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
+            encoder.encode_op (OpCode_callsubr);
+            break;
+            
+          case OpCode_callgsubr:
+            encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
+            encoder.encode_op (OpCode_callgsubr);
+            break;
+
+          default:
+            encoder.copy_str (opstr.str);
+            break;
+        }
+      }
+    }
+    return !encoder.is_error ();
+  }
+
+  protected:
+  SubrClosures              closures;
+
+  ParsedCStrs               parsed_charstrings;
+  ParsedCStrs               parsed_global_subrs;
+  hb_vector_t<ParsedCStrs>  parsed_local_subrs;
+
+  SubrRemaps                remaps;
+
+  private:
+  typedef typename SUBRS::count_type subr_count_type;
+};
 };  /* namespace CFF */
 
 HB_INTERNAL bool
index 4216be4..4654ac9 100644 (file)
@@ -92,18 +92,18 @@ struct CFF1TopDictValuesMod : CFF1TopDictValues
     SUPER::fini ();
   }
 
-  inline unsigned getNumValues (void) const
+  inline unsigned get_count (void) const
   {
-    return base->getNumValues () + SUPER::getNumValues ();
+    return base->get_count () + SUPER::get_count ();
   }
-  inline const CFF1TopDictVal &getValue (unsigned int i) const
+  inline const CFF1TopDictVal &get_value (unsigned int i) const
   {
-    if (i < base->getNumValues ())
+    if (i < base->get_count ())
       return (*base)[i];
     else
-      return SUPER::values[i - base->getNumValues ()];
+      return SUPER::values[i - base->get_count ()];
   }
-  inline const CFF1TopDictVal &operator [] (unsigned int i) const { return getValue (i); }
+  inline const CFF1TopDictVal &operator [] (unsigned int i) const { return get_value (i); }
 
   inline void reassignSIDs (const RemapSID& sidmap)
   {
@@ -230,9 +230,9 @@ struct FontDictValuesMod
     privateDictInfo = privateDictInfo_;
   }
 
-  inline unsigned getNumValues (void) const
+  inline unsigned get_count (void) const
   {
-    return base->getNumValues ();
+    return base->get_count ();
   }
 
   inline const OpStr &operator [] (unsigned int i) const { return (*base)[i]; }
@@ -270,10 +270,9 @@ struct CFF1FontDict_OpSerializer : CFFFontDict_OpSerializer
 
 struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
 {
-  static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
+  static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
   {
-    start_arg = env.check_width ();
-    if ((start_arg > 0) && likely (param.flatStr.len == 0))
+    if (env.arg_start > 0)
       flush_width (env, param);
 
     switch (op)
@@ -292,35 +291,40 @@ struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
         HB_FALLTHROUGH;
         
       default:
-        SUPER::flush_args_and_op (op, env, param, start_arg);
+        SUPER::flush_args_and_op (op, env, param);
         break;
     }
   }
-
-  static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
+  static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param)
   {
-    for (unsigned int i = start_arg; i < env.argStack.get_count (); i++)
-      param.flatStr.encode_num (env.eval_arg (i));
-    SUPER::flush_args (env, param, start_arg);
+    StrEncoder  encoder (param.flatStr);
+    for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
+      encoder.encode_num (env.eval_arg (i));
+    SUPER::flush_args (env, param);
   }
 
   static inline void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
   {
-    param.flatStr.encode_op (op);
+    StrEncoder  encoder (param.flatStr);
+    encoder.encode_op (op);
   }
 
   static inline void flush_width (CFF1CSInterpEnv &env, FlattenParam& param)
   {
     assert (env.has_width);
-    param.flatStr.encode_num (env.width);
+    StrEncoder  encoder (param.flatStr);
+    encoder.encode_num (env.width);
   }
 
   static inline void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
   {
     SUPER::flush_hintmask (op, env, param);
     if (!param.drop_hints)
+    {
+      StrEncoder  encoder (param.flatStr);
       for (unsigned int i = 0; i < env.hintmask_size; i++)
-        param.flatStr.encode_byte (env.substr[i]);
+        encoder.encode_byte (env.substr[i]);
+    }
   }
 
   private:
@@ -342,6 +346,70 @@ struct RangeList : hb_vector_t<code_pair>
   }
 };
 
+struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam>
+{
+  static inline void process_op (OpCode op, CFF1CSInterpEnv &env, SubrSubsetParam& param)
+  {
+    switch (op) {
+
+      case OpCode_return:
+        param.current_parsed_str->add_op (op, env.substr);
+        param.current_parsed_str->set_parsed ();
+        env.returnFromSubr ();
+        param.set_current_str (env);
+        break;
+
+      case OpCode_endchar:
+        param.current_parsed_str->add_op (op, env.substr);
+        param.current_parsed_str->set_parsed ();
+        SUPER::process_op (op, env, param);
+        break;
+
+      case OpCode_callsubr:
+        process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
+        break;
+
+      case OpCode_callgsubr:
+        process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
+        break;
+
+      default:
+        SUPER::process_op (op, env, param);
+        param.current_parsed_str->add_op (op, env.substr);
+        break;
+    }
+  }
+
+  static inline void process_width (CFF1CSInterpEnv &env, SubrSubsetParam& param)
+  {
+  
+  }
+
+  protected:
+  static inline void process_call_subr (OpCode op, CSType type,
+                                        CFF1CSInterpEnv &env, SubrSubsetParam& param,
+                                        CFF1BiasedSubrs& subrs, hb_set_t *closure)
+  {
+    SubByteStr    substr = env.substr;
+    env.callSubr (subrs, type);
+    param.current_parsed_str->addCallOp (op, substr, env.context.subr_num);
+    hb_set_add (closure, env.context.subr_num);
+    param.set_current_str (env);
+  }
+
+  private:
+  typedef CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
+};
+
+struct CFF1SubrSubsetter : SubrSubsetter<CFF1SubrSubsetter, CFF1Subrs, const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubrSubset>
+{
+  static inline void set_parsed_prefix (const CFF1CSInterpEnv &env, ParsedCStr &charstring)
+  {
+    if (env.processed_width)
+      charstring.set_prefix (env.width);
+  }
+};
+
 struct cff_subset_plan {
   inline cff_subset_plan (void)
     : final_size (0),
@@ -349,7 +417,8 @@ struct cff_subset_plan {
       orig_fdcount (0),
       subset_fdcount (1),
       subset_fdselect_format (0),
-      drop_hints (false)
+      drop_hints (false),
+      desubroutinize(false)
   {
     topdict_sizes.init ();
     topdict_sizes.resize (1);
@@ -357,7 +426,8 @@ struct cff_subset_plan {
     subset_fdselect_ranges.init ();
     fdmap.init ();
     subset_charstrings.init ();
-    flat_charstrings.init ();
+    subset_globalsubrs.init ();
+    subset_localsubrs.init ();
     fontdicts_mod.init ();
     subset_enc_code_ranges.init ();
     subset_enc_supp_codes.init ();
@@ -374,7 +444,8 @@ struct cff_subset_plan {
     subset_fdselect_ranges.fini ();
     fdmap.fini ();
     subset_charstrings.fini ();
-    flat_charstrings.fini ();
+    subset_globalsubrs.fini ();
+    subset_localsubrs.fini_deep ();
     fontdicts_mod.fini ();
     subset_enc_code_ranges.fini ();
     subset_enc_supp_codes.init ();
@@ -504,9 +575,9 @@ struct cff_subset_plan {
     }
 
     if (acc.fdArray != &Null(CFF1FDArray))
-      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
-        if (!fdmap.excludes (fd))
-          (void)sidmap.add (acc.fontDicts[fd].fontName);
+      for (unsigned int i = 0; i < orig_fdcount; i++)
+        if (fdmap.includes (i))
+          (void)sidmap.add (acc.fontDicts[i].fontName);
   
     return true;
   }
@@ -521,6 +592,7 @@ struct cff_subset_plan {
     num_glyphs = plan->glyphs.len;
     orig_fdcount = acc.fdCount;
     drop_hints = plan->drop_hints;
+    desubroutinize = true; // plan->desubroutinize;
 
     /* check whether the subset renumbers any glyph IDs */
     gid_renum = false;
@@ -546,14 +618,14 @@ struct cff_subset_plan {
     {
       /* Add encoding/charset to a (copy of) top dict as necessary */
       topdict_mod.init (&acc.topDict);
-      bool need_to_add_enc = (subset_encoding && !acc.topDict.hasOp (OpCode_Encoding));
-      bool need_to_add_set = (subset_charset && !acc.topDict.hasOp (OpCode_charset));
+      bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
+      bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
       if (need_to_add_enc || need_to_add_set)
       {
         if (need_to_add_enc)
-          topdict_mod.addOp (OpCode_Encoding);
+          topdict_mod.add_op (OpCode_Encoding);
         if (need_to_add_set)
-          topdict_mod.addOp (OpCode_charset);
+          topdict_mod.add_op (OpCode_charset);
       }
       offsets.topDictInfo.offset = final_size;
       CFF1TopDict_OpSerializer topSzr;
@@ -595,16 +667,54 @@ struct cff_subset_plan {
       final_size += offsets.stringIndexInfo.size;
     }
     
+    if (desubroutinize)
     {
       /* Flatten global & local subrs */
       SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten>
                     flattener(acc, plan->glyphs, plan->drop_hints);
-      if (!flattener.flatten (flat_charstrings))
+      if (!flattener.flatten (subset_charstrings))
         return false;
       
       /* no global/local subroutines */
       offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */
     }
+    else
+    {
+      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
+      if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
+        return false;
+
+      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
+      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
+        return false;
+
+      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
+        return false;
+
+      /* global subrs */
+      unsigned int dataSize = subset_globalsubrs.total_size ();
+      offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
+      offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
+
+      /* local subrs */
+      if (!offsets.localSubrsInfos.resize (orig_fdcount))
+        return false;
+      if (!subset_localsubrs.resize (orig_fdcount))
+        return false;
+      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
+      {
+        subset_localsubrs[fd].init ();
+        if (fdmap.includes (fd))
+        {
+          if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
+            return false;
+
+          unsigned int dataSize = subset_localsubrs[fd].total_size ();
+          offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
+          offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
+        }
+      }
+    }
 
     /* global subrs */
     offsets.globalSubrsInfo.offset = final_size;
@@ -640,7 +750,7 @@ struct cff_subset_plan {
       CFF1FontDict_OpSerializer fontSzr;
       unsigned int dictsSize = 0;
       for (unsigned int i = 0; i < acc.fontDicts.len; i++)
-        if (!fdmap.excludes (i))
+        if (fdmap.includes (i))
           dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
 
       offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
@@ -650,14 +760,7 @@ struct cff_subset_plan {
     /* CharStrings */
     {
       offsets.charStringsInfo.offset = final_size;
-      unsigned int dataSize = 0;
-      for (unsigned int i = 0; i < plan->glyphs.len; i++)
-      {
-        ByteStrBuff &flatstr = flat_charstrings[i];
-        ByteStr str (&flatstr[0], flatstr.len);
-        subset_charstrings.push (str);
-        dataSize += flatstr.len;
-      }
+      unsigned int dataSize = subset_charstrings.total_size ();
       offsets.charStringsInfo.offSize = calcOffSize (dataSize);
       final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
     }
@@ -666,23 +769,26 @@ struct cff_subset_plan {
     offsets.privateDictInfo.offset = final_size;
     for (unsigned int i = 0; i < orig_fdcount; i++)
     {
-      if (!fdmap.excludes (i))
+      if (fdmap.includes (i))
       {
-        CFFPrivateDict_OpSerializer privSzr (plan->drop_hints);
+        CFFPrivateDict_OpSerializer privSzr (desubroutinize, plan->drop_hints);
         unsigned int  priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
         TableInfo  privInfo = { final_size, priv_size, 0 };
         FontDictValuesMod fontdict_mod;
         fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo );
         fontdicts_mod.push (fontdict_mod);
         final_size += privInfo.size;
+
+        if (!plan->desubroutinize)
+          final_size += offsets.localSubrsInfos[i].size;
       }
     }
 
     if (!acc.is_CID ())
       offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
 
-    return ((subset_charstrings.len == plan->glyphs.len) &&
-            (fontdicts_mod.len == subset_fdcount));
+    return ((subset_charstrings.len == plan->glyphs.len)
+           && (fontdicts_mod.len == subset_fdcount));
   }
 
   inline unsigned int get_final_size (void) const  { return final_size; }
@@ -703,11 +809,11 @@ struct cff_subset_plan {
    * set to CFF_UNDEF_CODE if excluded from subset */
   Remap   fdmap;
 
-  hb_vector_t<ByteStr>    subset_charstrings;
-  ByteStrBuffArray        flat_charstrings;
+  StrBuffArray            subset_charstrings;
+  StrBuffArray            subset_globalsubrs;
+  hb_vector_t<StrBuffArray> subset_localsubrs;
   hb_vector_t<FontDictValuesMod>  fontdicts_mod;
 
-  bool                    flatten_subrs;
   bool                    drop_hints;
 
   bool                    gid_renum;
@@ -723,6 +829,9 @@ struct cff_subset_plan {
 
   RemapSID                sidmap;
   unsigned int            topDictModSIDs[NameDictValues::ValCount];
+
+  bool                    desubroutinize;
+  CFF1SubrSubsetter       subr_subsetter;
 };
 
 static inline bool _write_cff1 (const cff_subset_plan &plan,
@@ -790,9 +899,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
   {
     assert (plan.offsets.globalSubrsInfo.offset != 0);
     assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
-    CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
-    if (unlikely (dest == nullptr)) return false;
-    dest->count.set (0);
+    
+    if (plan.desubroutinize)
+    {
+      CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
+      if (unlikely (dest == nullptr)) return false;
+      dest->count.set (0);
+    }
+    else
+    {
+      CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
+      if (unlikely (dest == nullptr)) return false;
+      if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
+        return false;
+      }
+    }
   }
 
   /* Encoding */
@@ -893,7 +1016,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
       if (unlikely (pd == nullptr)) return false;
       unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size;
       bool result;
-      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
+      CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
       /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
       result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
       if (unlikely (!result))
@@ -904,6 +1027,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
     }
   }
 
+  if (!plan.desubroutinize)
+  {
+    for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+    {
+      if (!plan.fdmap.excludes (i))
+      {
+        CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
+        if (unlikely (dest == nullptr)) return false;
+        if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
+        {
+          DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
+          return false;
+        }
+      }
+    }
+  }
+
   assert (c.head == c.end);
   c.end_serialize ();
 
index b30145c..22a35c5 100644 (file)
@@ -77,7 +77,7 @@ struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<>
 
 struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
 {
-  static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
+  static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
   {
     switch (op)
     {
@@ -100,29 +100,30 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
         HB_FALLTHROUGH;
         
       default:
-        SUPER::flush_args_and_op (op, env, param, start_arg);
+        SUPER::flush_args_and_op (op, env, param);
         break;
     }
   }
 
-  static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
+  static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param)
   {
-    for (unsigned int i = start_arg; i < env.argStack.get_count ();)
+    for (unsigned int i = 0; i < env.argStack.get_count ();)
     {
       const BlendArg &arg = env.argStack[i];
       if (arg.blending ())
       {
-        assert ((arg.numValues > 0) && (env.argStack.get_count () - start_arg >= arg.numValues));
+        assert ((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues));
         flatten_blends (arg, i, env, param);
         i += arg.numValues;
       }
       else
       {
-        param.flatStr.encode_num (arg);
+        StrEncoder  encoder (param.flatStr);
+        encoder.encode_num (arg);
         i++;
       }
     }
-    SUPER::flush_args (env, param, start_arg);
+    SUPER::flush_args (env, param);
   }
 
   static inline void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param)
@@ -133,20 +134,22 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
       const BlendArg &arg1 = env.argStack[i + j];
       assert (arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
               (arg1.deltas.len == env.get_region_count ()));
-      param.flatStr.encode_num (arg1);
+      StrEncoder  encoder (param.flatStr);
+      encoder.encode_num (arg1);
     }
     /* flatten deltas for each value */
+    StrEncoder  encoder (param.flatStr);
     for (unsigned int j = 0; j < arg.numValues; j++)
     {
       const BlendArg &arg1 = env.argStack[i + j];
       for (unsigned int k = 0; k < arg1.deltas.len; k++)
-        param.flatStr.encode_num (arg1.deltas[k]);
+        encoder.encode_num (arg1.deltas[k]);
     }
     /* flatten the number of values followed by blend operator */
-    param.flatStr.encode_int (arg.numValues);
-    param.flatStr.encode_op (OpCode_blendcs);
+    encoder.encode_int (arg.numValues);
+    encoder.encode_op (OpCode_blendcs);
   }
-
   static inline void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
   {
     switch (op)
@@ -155,7 +158,8 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
       case OpCode_endchar:
         return;
       default:
-        param.flatStr.encode_op (op);
+        StrEncoder  encoder (param.flatStr);
+        encoder.encode_op (op);
     }
   }
 
@@ -170,7 +174,8 @@ struct cff2_subset_plan {
       orig_fdcount (0),
       subset_fdcount(1),
       subset_fdselect_format (0),
-      drop_hints (false)
+      drop_hints (false),
+      desubroutinize (false)
   {
     subset_fdselect_ranges.init ();
     fdmap.init ();
@@ -195,6 +200,7 @@ struct cff2_subset_plan {
     orig_fdcount = acc.fdArray->count;
 
     drop_hints = plan->drop_hints;
+    desubroutinize = plan->desubroutinize;
 
     /* CFF2 header */
     final_size += OT::cff2::static_size;
@@ -260,7 +266,7 @@ struct cff2_subset_plan {
       unsigned int dataSize = 0;
       for (unsigned int i = 0; i < plan->glyphs.len; i++)
       {
-        ByteStrBuff &flatstr = flat_charstrings[i];
+        StrBuff &flatstr = flat_charstrings[i];
         ByteStr str (&flatstr[0], flatstr.len);
         subset_charstrings.push (str);
         dataSize += flatstr.len;
@@ -276,7 +282,7 @@ struct cff2_subset_plan {
       if (!fdmap.excludes (i))
       {
         unsigned int priv_size;
-        CFFPrivateDict_OpSerializer privSzr (drop_hints);
+        CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints);
         priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
         TableInfo  privInfo = { final_size, priv_size, 0 };
         privateDictInfos.push (privInfo);
@@ -300,11 +306,12 @@ struct cff2_subset_plan {
 
   Remap   fdmap;
 
-  hb_vector_t<ByteStr>    subset_charstrings;
-  ByteStrBuffArray        flat_charstrings;
+  ByteStrArray            subset_charstrings;
+  StrBuffArray            flat_charstrings;
   hb_vector_t<TableInfo>  privateDictInfos;
 
   bool            drop_hints;
+  bool            desubroutinize;
 };
 
 static inline bool _write_cff2 (const cff2_subset_plan &plan,
@@ -421,7 +428,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
       if (unlikely (pd == nullptr)) return false;
       unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
       bool result;
-      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
+      CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
       result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
       if (unlikely (!result))
       {
index 9fc8615..75edd5d 100644 (file)
@@ -44,6 +44,7 @@ struct hb_subset_input_t
 
   bool drop_hints : 1;
   bool drop_layout : 1;
+  bool desubroutinize : 1;
   /* TODO
    *
    * features
index 0570060..71c4a46 100644 (file)
@@ -150,6 +150,7 @@ hb_subset_plan_create (hb_face_t           *face,
 
   plan->drop_hints = input->drop_hints;
   plan->drop_layout = input->drop_layout;
+  plan->desubroutinize = input->desubroutinize;
   plan->unicodes = hb_set_create();
   plan->glyphs.init();
   plan->source = hb_face_reference (face);
index c2c484a..c14f1bb 100644 (file)
@@ -41,6 +41,7 @@ struct hb_subset_plan_t
 
   bool drop_hints : 1;
   bool drop_layout : 1;
+  bool desubroutinize : 1;
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
index 5661cd0..e52d63b 100644 (file)
@@ -970,6 +970,8 @@ subset_options_t::add_options (option_parser_t *parser)
   GOptionEntry entries[] =
   {
     {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->drop_hints,   "Whether to drop hints",   nullptr},
+    {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->desubroutinize,   "Remove CFF/CFF2 use of subroutinizes",   nullptr},
+
     {nullptr}
   };
   parser->add_group (entries,
index dd62859..f90b22f 100644 (file)
@@ -669,6 +669,7 @@ struct subset_options_t : option_group_t
   subset_options_t (option_parser_t *parser)
   {
     drop_hints = false;
+    desubroutinize = false;
 
     add_options (parser);
   }
@@ -676,6 +677,7 @@ struct subset_options_t : option_group_t
   void add_options (option_parser_t *parser);
 
   hb_bool_t drop_hints;
+  hb_bool_t desubroutinize;
 };
 
 /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */