Added CFF2 Subr nullifier

along with CFF2 charstring interpreter
factored out common code between CFF1 & CFF2 to CSInterpreter
moved fetch_op from Interpreter to InterpEnv
misc code clean up & bug fixes
pull/1113/head
Michiharu Ariza 6 years ago
parent f57d6bcbca
commit cef75ea41a
  1. 1
      src/Makefile.sources
  2. 44
      src/hb-cff-interp-common-private.hh
  3. 95
      src/hb-cff-interp-cs-common-private.hh
  4. 3
      src/hb-cff-interp-dict-common-private.hh
  5. 82
      src/hb-cff1-interp-cs.hh
  6. 97
      src/hb-cff2-interp-cs.hh
  7. 4
      src/hb-ot-cff-common-private.hh
  8. 7
      src/hb-ot-cff2-table.hh
  9. 1
      src/hb-subset-cff1.cc
  10. 62
      src/hb-subset-cff2.cc

@ -155,6 +155,7 @@ HB_OT_sources = \
hb-cff-interp-common-private.hh \
hb-cff-interp-cs-common-private.hh \
hb-cff1-interp-cs.hh \
hb-cff2-interp-cs.hh \
hb-cff-interp-dict-common-private.hh \
$(NULL)

@ -56,7 +56,7 @@ enum OpCode {
OpCode_Subrs, /* 19 CFF Private, CFF2 Private */
OpCode_defaultWidthX, /* 20 CFF Private (0) */
OpCode_nominalWidthX, /* 21 CFF Private (0) */
OpCode_vsindex, /* 22 CFF2 Private/CS */
OpCode_vsindexdict, /* 22 CFF2 Private/CS */
OpCode_blenddict, /* 23 CFF2 Private/CS */
OpCode_vstore, /* 24 CFF2 Top */
OpCode_reserved25, /* 25 */
@ -142,8 +142,8 @@ enum OpCode {
// OpCode_escape, /* 12 CFF, CFF2 */
OpCode_Reserved13 = 13,
OpCode_endchar, /* 14 CFF */
// OpCode_vsindex, /* 15 CFF2 */
OpCode_blendcs = 16, /* 16 CFF2 */
OpCode_vsindexcs, /* 15 CFF2 */
OpCode_blendcs, /* 16 CFF2 */
OpCode_Reserved17,
OpCode_hstemhm, /* 18 CFF, CFF2 */
OpCode_hintmask, /* 19 CFF, CFF2 */
@ -365,8 +365,8 @@ struct Stack
inline void clear (void) { size = 0; }
inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
inline bool check_underflow (unsigned int count) const { return (count <= size); }
inline bool check_overflow (unsigned int count=1) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
inline bool check_underflow (unsigned int count=1) const { return (count <= size); }
inline unsigned int get_size (void) const { return size; }
inline bool is_empty (void) const { return size == 0; }
@ -396,7 +396,7 @@ struct ArgStack : Stack<Number, 513>
inline bool check_pop_num (Number& n)
{
if (unlikely (!this->check_underflow (1)))
if (unlikely (!this->check_underflow ()))
return false;
n = this->pop ();
return true;
@ -413,7 +413,7 @@ struct ArgStack : Stack<Number, 513>
inline bool check_pop_int (int& v)
{
if (unlikely (!this->check_underflow (1)))
if (unlikely (!this->check_underflow ()))
return false;
v = this->pop ().to_int ();
return true;
@ -501,6 +501,21 @@ struct InterpEnv
argStack.fini ();
}
inline bool fetch_op (OpCode &op)
{
if (unlikely (!substr.avail ()))
return false;
op = (OpCode)(unsigned char)substr[0];
if (op == OpCode_escape) {
if (unlikely (!substr.avail ()))
return false;
op = Make_OpCode_ESC (substr[1]);
substr.inc ();
}
substr.inc ();
return true;
}
SubByteStr substr;
ArgStack argStack;
};
@ -558,21 +573,6 @@ struct Interpreter {
inline void fini (void) { env.fini (); }
inline bool fetch_op (OpCode &op)
{
if (unlikely (!env.substr.avail ()))
return false;
op = (OpCode)(unsigned char)env.substr[0];
if (op == OpCode_escape) {
if (unlikely (!env.substr.avail ()))
return false;
op = Make_OpCode_ESC (env.substr[1]);
env.substr.inc ();
}
env.substr.inc ();
return true;
}
ENV env;
};

@ -64,6 +64,11 @@ struct CSInterpEnv : InterpEnv
{
InterpEnv::init (str);
stack_cleared = false;
seen_moveto = true;
seen_hintmask = false;
hstem_count = 0;
vstem_count = 0;
callStack.init ();
globalSubrs.init (globalSubrs_);
localSubrs.init (localSubrs_);
@ -105,20 +110,55 @@ struct CSInterpEnv : InterpEnv
inline bool returnFromSubr (void)
{
if (unlikely (!callStack.check_underflow (1)))
if (unlikely (!callStack.check_underflow ()))
return false;
substr = callStack.pop ();
return true;
}
inline void determine_hintmask_size (void)
{
if (!seen_hintmask)
{
vstem_count += argStack.size / 2;
hintmask_size = (hstem_count + vstem_count + 7) >> 3;
seen_hintmask = true;
}
clear_stack ();
}
inline void process_moveto (void)
{
clear_stack ();
if (!seen_moveto)
{
determine_hintmask_size ();
seen_moveto = true;
}
}
inline void clear_stack (void)
{
stack_cleared = true;
argStack.clear ();
}
inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
inline bool is_endchar (void) const { return endchar_flag; }
inline bool is_stack_cleared (void) const { return stack_cleared; }
protected:
bool endchar_flag;
bool endchar_flag;
bool stack_cleared;
bool seen_moveto;
bool seen_hintmask;
public:
unsigned int hstem_count;
unsigned int vstem_count;
unsigned int hintmask_size;
CallStack callStack;
BiasedSubrs<SUBRS> globalSubrs;
BiasedSubrs<SUBRS> localSubrs;
@ -131,6 +171,12 @@ struct CSOpSet : OpSet
{
switch (op) {
case OpCode_return:
return env.returnFromSubr ();
case OpCode_endchar:
env.set_endchar (true);
return true;
case OpCode_longintcs:
return env.argStack.push_longint_from_substr (env.substr);
@ -140,9 +186,50 @@ struct CSOpSet : OpSet
case OpCode_callgsubr:
return env.callSubr (env.globalSubrs);
case OpCode_hstem:
case OpCode_hstemhm:
env.hstem_count += env.argStack.size / 2;
env.clear_stack ();
break;
case OpCode_vstem:
case OpCode_vstemhm:
env.vstem_count += env.argStack.size / 2;
env.clear_stack ();
break;
case OpCode_hintmask:
case OpCode_cntrmask:
env.determine_hintmask_size ();
if (unlikely (!env.substr.avail (env.hintmask_size)))
return false;
env.substr.inc (env.hintmask_size);
break;
case OpCode_vmoveto:
case OpCode_rlineto:
case OpCode_hlineto:
case OpCode_vlineto:
case OpCode_rmoveto:
case OpCode_hmoveto:
env.process_moveto ();
break;
case OpCode_rrcurveto:
case OpCode_rcurveline:
case OpCode_rlinecurve:
case OpCode_vvcurveto:
case OpCode_hhcurveto:
case OpCode_vhcurveto:
case OpCode_hvcurveto:
case OpCode_hflex:
case OpCode_flex:
case OpCode_hflex1:
case OpCode_flex1:
env.clear_stack ();
break;
default:
return OpSet::process_op (op, env);
}
return true;
}
};
@ -157,13 +244,11 @@ struct CSInterpreter : Interpreter<ENV>
for (;;) {
OpCode op;
if (unlikely (!super.fetch_op (op) ||
if (unlikely (!super.env.fetch_op (op) ||
!OPSET::process_op (op, super.env, param)))
return false;
if (super.env.is_endchar ())
break;
if (!super.env.substr.avail ())
return false;
}
return true;

@ -170,7 +170,8 @@ struct DictInterpreter : Interpreter<InterpEnv>
do
{
OpCode op;
if (unlikely (!super.fetch_op (op) || !OPSET::process_op (op, super.env, param)))
if (unlikely (!super.env.fetch_op (op) ||
!OPSET::process_op (op, super.env, param)))
return false;
} while (super.env.substr.avail ());

@ -38,11 +38,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs)
{
CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs);
seen_width = false;
seen_moveto = true;
seen_hintmask = false;
hstem_count = 0;
vstem_count = 0;
for (unsigned int i = 0; i < kTransientArraySize; i++)
transient_array[i].set_int (0);
}
@ -50,34 +45,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
bool check_transient_array_index (unsigned int i) const
{ return i < kTransientArraySize; }
inline void determine_hintmask_size (void)
{
if (!seen_hintmask)
{
vstem_count += argStack.size / 2;
hintmask_size = (hstem_count + vstem_count + 7) >> 3;
seen_hintmask = true;
}
clear_stack ();
}
inline void process_moveto (void)
{
clear_stack ();
if (!seen_moveto)
{
determine_hintmask_size ();
seen_moveto = true;
}
}
inline void clear_stack (void)
{
seen_width = true;
argStack.clear ();
}
inline void process_width (void)
{
if (!seen_width && (argStack.size > 0))
@ -90,11 +57,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
bool seen_width;
Number width;
bool seen_moveto;
bool seen_hintmask;
unsigned int hintmask_size;
unsigned int hstem_count;
unsigned int vstem_count;
static const unsigned int kTransientArraySize = 32;
Number transient_array[kTransientArraySize];
@ -109,11 +71,6 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
switch (op) {
case OpCode_return:
return env.returnFromSubr ();
case OpCode_endchar:
env.set_endchar (true);
return true;
case OpCode_and:
if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
env.argStack.push_int ((n1.to_real() != 0.0f) && (n2.to_real() != 0.0f));
@ -223,45 +180,6 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
}
}
break;
case OpCode_hstem:
case OpCode_hstemhm:
env.hstem_count += env.argStack.size / 2;
env.clear_stack ();
break;
case OpCode_vstem:
case OpCode_vstemhm:
env.vstem_count += env.argStack.size / 2;
env.clear_stack ();
break;
case OpCode_hintmask:
case OpCode_cntrmask:
env.determine_hintmask_size ();
if (unlikely (!env.substr.avail (env.hintmask_size)))
return false;
env.substr.inc (env.hintmask_size);
break;
case OpCode_vmoveto:
case OpCode_rlineto:
case OpCode_hlineto:
case OpCode_vlineto:
case OpCode_rmoveto:
case OpCode_hmoveto:
env.process_moveto ();
break;
case OpCode_rrcurveto:
case OpCode_rcurveline:
case OpCode_rlinecurve:
case OpCode_vvcurveto:
case OpCode_hhcurveto:
case OpCode_vhcurveto:
case OpCode_hvcurveto:
case OpCode_hflex:
case OpCode_flex:
case OpCode_hflex1:
case OpCode_flex1:
env.clear_stack ();
break;
default:
typedef CSOpSet<CFF1Subrs, PARAM> SUPER;
if (unlikely (!SUPER::process_op (op, env, param)))

@ -0,0 +1,97 @@
/*
* 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_CFF2_INTERP_CS_HH
#define HB_CFF2_INTERP_CS_HH
#include "hb-private.hh"
#include "hb-cff-interp-cs-common-private.hh"
namespace CFF {
using namespace OT;
struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
{
inline void init (const ByteStr &str, const CFF2Subrs &globalSubrs_, const CFF2Subrs &localSubrs_)
{
CSInterpEnv<CFF2Subrs>::init (str, globalSubrs_, localSubrs_);
ivs = 0;
}
inline bool fetch_op (OpCode &op)
{
if (unlikely (substr.avail ()))
return CSInterpEnv<CFF2Subrs>::fetch_op (op);
/* make up return or endchar op */
if (callStack.check_underflow ())
op = OpCode_return;
else
op = OpCode_endchar;
return true;
}
inline unsigned int get_ivs (void) const { return ivs; }
inline void set_ivs (unsigned int ivs_) { ivs = ivs_; }
protected:
unsigned int ivs;
};
template <typename PARAM>
struct CFF2CSOpSet : CSOpSet<CFF2Subrs, PARAM>
{
static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
{
switch (op) {
case OpCode_blendcs:
env.clear_stack (); // XXX: TODO
break;
case OpCode_vsindexcs:
{
unsigned int ivs;
if (unlikely (!env.argStack.check_pop_uint (ivs))) return false;
env.set_ivs (ivs);
env.clear_stack ();
}
break;
default:
typedef CSOpSet<CFF2Subrs, PARAM> SUPER;
if (unlikely (!SUPER::process_op (op, env, param)))
return false;
break;
}
return true;
}
};
template <typename OPSET, typename PARAM>
struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
} /* namespace CFF */
#endif /* HB_CFF2_INTERP_CS_HH */

@ -557,6 +557,8 @@ 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) || (hb_set_get_population (set) == 0))
{
if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
@ -580,6 +582,8 @@ struct Subrs : CFFIndex<COUNT>
/* 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))

@ -318,6 +318,7 @@ struct CFF2PrivateDictOpSet : DictOpSet
return false;
env.argStack.clear ();
break;
case OpCode_vsindexdict:
case OpCode_blenddict:
// XXX: TODO
return true;
@ -442,10 +443,11 @@ struct cff2
if (num_glyphs != sc.get_num_glyphs ())
{ fini (); return; }
privateDicts.resize (fdArray->count);
fdCount = fdArray->count;
privateDicts.resize (fdCount);
/* parse font dicts and gather private dicts */
for (unsigned int i = 0; i < fdArray->count; i++)
for (unsigned int i = 0; i < fdCount; i++)
{
const ByteStr fontDictStr = (*fdArray)[i];
if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
@ -500,6 +502,7 @@ struct cff2
const CFF2CharStrings *charStrings;
const CFF2FDArray *fdArray;
const CFF2FDSelect *fdSelect;
unsigned int fdCount;
hb_vector_t<CFF2FontDictValues> fontDicts;
hb_vector_t<PrivDictVal> privateDicts;

@ -555,6 +555,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
}
}
assert (c.head == c.end);
c.end_serialize ();
return true;

@ -30,6 +30,7 @@
#include "hb-subset-cff2.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common-private.hh"
#include "hb-cff2-interp-cs.hh"
using namespace CFF;
@ -37,6 +38,12 @@ struct CFF2SubTableOffsets {
inline CFF2SubTableOffsets (void)
{
memset (this, 0, sizeof(*this));
localSubrsInfos.init ();
}
inline ~CFF2SubTableOffsets (void)
{
localSubrsInfos.fini ();
}
unsigned int topDictSize;
@ -45,6 +52,8 @@ struct CFF2SubTableOffsets {
TableInfo FDArrayInfo;
TableInfo charStringsInfo;
unsigned int privateDictsOffset;
TableInfo globalSubrsInfo;
hb_vector_t<TableInfo> localSubrsInfos;
};
struct CFF2TopDict_OpSerializer : OpSerializer
@ -157,6 +166,31 @@ struct CFF2PrivateDict_OpSerializer : OpSerializer
}
};
struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<SubrRefMapPair>
{
static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair)
{
unsigned int subr_num;
switch (op) {
case OpCode_callsubr:
if (!unlikely (env.popSubrNum(env.localSubrs, subr_num)))
return false;
env.argStack.unpop ();
refMapPair.local_map->add (subr_num);
break;
case OpCode_callgsubr:
if (!unlikely (env.popSubrNum(env.globalSubrs, subr_num)))
return false;
env.argStack.unpop ();
refMapPair.global_map->add (subr_num);
break;
default:
break;
}
return CFF2CSOpSet<SubrRefMapPair>::process_op (op, env, refMapPair);
}
};
struct cff2_subset_plan {
inline cff2_subset_plan (void)
: final_size (0),
@ -176,6 +210,7 @@ struct cff2_subset_plan {
fdmap.fini ();
subset_charstrings.fini ();
privateDictInfos.fini ();
subrRefMaps.fini ();
}
inline bool create (const OT::cff2::accelerator_subset_t &acc,
@ -194,8 +229,22 @@ struct cff2_subset_plan {
final_size += offsets.topDictSize;
}
/* Subset global & local subrs */
{
SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset> subsetter(acc, plan->glyphs);
if (!subsetter.collect_refs (subrRefMaps))
return false;
offsets.globalSubrsInfo.size = acc.globalSubrs->calculate_serialized_size (offsets.globalSubrsInfo.offSize, subrRefMaps.global_map);
if (!offsets.localSubrsInfos.resize (orig_fdcount))
return false;
for (unsigned int i = 0; i < orig_fdcount; i++)
offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i]);
}
/* global subrs */
final_size += acc.globalSubrs->get_size ();
offsets.globalSubrsInfo.offset = final_size;
final_size += offsets.globalSubrsInfo.size;
/* variation store */
if (acc.varStore != &Null(CFF2VariationStore))
@ -253,7 +302,7 @@ struct cff2_subset_plan {
CFF2PrivateDict_OpSerializer privSzr;
TableInfo privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr), 0 };
privateDictInfos.push (privInfo);
final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
final_size += privInfo.size + offsets.localSubrsInfos[i].size;
}
}
@ -275,6 +324,8 @@ struct cff2_subset_plan {
hb_vector_t<ByteStr> subset_charstrings;
hb_vector_t<TableInfo> privateDictInfos;
SubrRefMaps subrRefMaps;
};
static inline bool _write_cff2 (const cff2_subset_plan &plan,
@ -312,8 +363,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
CFFIndex<HBUINT32> *super = dest;
if (unlikely (!super->serialize (&c, *acc.globalSubrs)))
if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 global subrs");
return false;
@ -409,8 +459,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
return false;
}
CFFIndex<HBUINT32> *super = subrs;
if (unlikely (!super->serialize (&c, *acc.privateDicts[i].localSubrs)))
if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs, plan.offsets.localSubrsInfos[i].offSize, plan.subrRefMaps.local_maps[i])))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 local subrs [%d]", i);
return false;
@ -419,6 +468,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
}
}
assert (c.head == c.end);
c.end_serialize ();
return true;

Loading…
Cancel
Save