Subroutine flattener for CFF1

Subr-flattened charstrings are temporarily re-encoded in ByteStrBuff during "plan" phase, then copied to hb_serialize_context_t during "write" phase

CSOpSet may callback opcode processing "virtual" functions via CRTP

Numer struct may store a value as fixed optionally in addition to int and float
This commit is contained in:
Michiharu Ariza 2018-08-29 12:14:30 -07:00
parent 27c32d8afb
commit a11420b48c
8 changed files with 372 additions and 110 deletions

View File

@ -204,23 +204,62 @@ enum OpCode {
}; };
inline OpCode Make_OpCode_ESC (unsigned char byte2) { return (OpCode)(OpCode_ESC_Base + byte2); } inline OpCode Make_OpCode_ESC (unsigned char byte2) { return (OpCode)(OpCode_ESC_Base + byte2); }
inline unsigned int OpCode_Size (OpCode op) { return (op >= OpCode_ESC_Base)? 2: 1; } inline OpCode Unmake_OpCode_ESC (OpCode op) { return (OpCode)(op - OpCode_ESC_Base); }
inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; }
inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op)? 2: 1; }
struct Number struct Number
{ {
inline Number (void) { set_int (0); } inline Number (void) { set_int (0); }
inline void set_int (int v) { is_real = false; u.int_val = v; }; inline void set_int (int v) { format = NumInt; u.int_val = v; };
inline int to_int (void) const { return is_real? (int)u.real_val: u.int_val; } inline int to_int (void) const { return is_int ()? u.int_val: (int)to_real (); }
inline void set_real (float v) { is_real = true; u.real_val = v; }; inline void set_fixed (int32_t v) { format = NumFixed; u.fixed_val = v; };
inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; } inline int32_t to_fixed (void) const
{
if (is_fixed ())
return u.fixed_val;
else if (is_real ())
return (int32_t)(u.real_val * 65536.0);
else
return (int32_t)(u.int_val << 16);
}
inline void set_real (float v) { format = NumReal; u.real_val = v; };
inline float to_real (void) const
{
if (is_real ())
return u.real_val;
if (is_fixed ())
return u.fixed_val / 65536.0;
else
return (float)u.int_val;
}
inline bool in_int_range (void) const
{
if (is_int ())
return true;
if (is_fixed () && ((u.fixed_val & 0xFFFF) == 0))
return true;
else
return ((float)(int16_t)to_int () == u.real_val);
}
protected: protected:
bool is_real; enum NumFormat {
NumInt,
NumFixed,
NumReal
};
NumFormat format;
union { union {
int int_val; int int_val;
int32_t fixed_val;
float real_val; float real_val;
} u; } u;
inline bool is_int (void) const { return format == NumInt; }
inline bool is_fixed (void) const { return format == NumFixed; }
inline bool is_real (void) const { return format == NumReal; }
}; };
/* byte string */ /* byte string */
@ -308,7 +347,7 @@ struct SubByteStr
offset = offset_; offset = offset_;
} }
inline const HBUINT8& operator [] (unsigned int i) const { inline const HBUINT8& operator [] (int i) const {
return str[offset + i]; return str[offset + i];
} }

View File

@ -125,18 +125,6 @@ struct CSInterpEnv : InterpEnv
hintmask_size = (hstem_count + vstem_count + 7) >> 3; hintmask_size = (hstem_count + vstem_count + 7) >> 3;
seen_hintmask = true; 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) inline void clear_stack (void)
@ -149,13 +137,12 @@ struct CSInterpEnv : InterpEnv
inline bool is_endchar (void) const { return endchar_flag; } inline bool is_endchar (void) const { return endchar_flag; }
inline bool is_stack_cleared (void) const { return stack_cleared; } inline bool is_stack_cleared (void) const { return stack_cleared; }
protected: public:
bool endchar_flag; bool endchar_flag;
bool stack_cleared; bool stack_cleared;
bool seen_moveto; bool seen_moveto;
bool seen_hintmask; bool seen_hintmask;
public:
unsigned int hstem_count; unsigned int hstem_count;
unsigned int vstem_count; unsigned int vstem_count;
unsigned int hintmask_size; unsigned int hintmask_size;
@ -164,11 +151,11 @@ struct CSInterpEnv : InterpEnv
BiasedSubrs<SUBRS> localSubrs; BiasedSubrs<SUBRS> localSubrs;
}; };
template <typename SUBRS, typename PARAM> template <typename OPSET, typename ENV, typename PARAM>
struct CSOpSet : OpSet struct CSOpSet : OpSet
{ {
static inline bool process_op (OpCode op, CSInterpEnv<SUBRS> &env, PARAM& param) static inline bool process_op (OpCode op, ENV &env, PARAM& param)
{ {
switch (op) { switch (op) {
case OpCode_return: case OpCode_return:
@ -188,17 +175,16 @@ struct CSOpSet : OpSet
case OpCode_hstem: case OpCode_hstem:
case OpCode_hstemhm: case OpCode_hstemhm:
env.hstem_count += env.argStack.size / 2; OPSET::process_hstem (env, param);
env.clear_stack ();
break; break;
case OpCode_vstem: case OpCode_vstem:
case OpCode_vstemhm: case OpCode_vstemhm:
env.vstem_count += env.argStack.size / 2; OPSET::process_vstem (env, param);
env.clear_stack ();
break; break;
case OpCode_hintmask: case OpCode_hintmask:
case OpCode_cntrmask: case OpCode_cntrmask:
env.determine_hintmask_size (); env.determine_hintmask_size ();
OPSET::flush_stack (env, param);
if (unlikely (!env.substr.avail (env.hintmask_size))) if (unlikely (!env.substr.avail (env.hintmask_size)))
return false; return false;
env.substr.inc (env.hintmask_size); env.substr.inc (env.hintmask_size);
@ -210,7 +196,7 @@ struct CSOpSet : OpSet
case OpCode_vlineto: case OpCode_vlineto:
case OpCode_rmoveto: case OpCode_rmoveto:
case OpCode_hmoveto: case OpCode_hmoveto:
env.process_moveto (); OPSET::process_moveto (env, param);
break; break;
case OpCode_rrcurveto: case OpCode_rrcurveto:
case OpCode_rcurveline: case OpCode_rcurveline:
@ -223,7 +209,7 @@ struct CSOpSet : OpSet
case OpCode_flex: case OpCode_flex:
case OpCode_hflex1: case OpCode_hflex1:
case OpCode_flex1: case OpCode_flex1:
env.clear_stack (); OPSET::flush_stack (env, param);
break; break;
default: default:
@ -231,6 +217,83 @@ struct CSOpSet : OpSet
} }
return true; return true;
} }
static inline void process_hstem (ENV &env, PARAM& param)
{
env.hstem_count += env.argStack.size / 2;
OPSET::flush_stack (env, param);
}
static inline void process_vstem (ENV &env, PARAM& param)
{
env.vstem_count += env.argStack.size / 2;
OPSET::flush_stack (env, param);
}
static inline void process_moveto (ENV &env, PARAM& param)
{
if (!env.seen_moveto)
{
env.determine_hintmask_size ();
env.seen_moveto = true;
}
OPSET::flush_stack (env, param);
}
static inline void flush_stack (ENV &env, PARAM& param)
{
env.clear_stack ();
}
/* numeric / logical / arithmetic operators */
static inline bool is_arg_op (OpCode op)
{
switch (op)
{
case OpCode_shortint:
case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
case OpCode_fixedcs:
case OpCode_and:
case OpCode_or:
case OpCode_not:
case OpCode_abs:
case OpCode_add:
case OpCode_sub:
case OpCode_div:
case OpCode_neg:
case OpCode_eq:
case OpCode_drop:
case OpCode_put:
case OpCode_get:
case OpCode_ifelse:
case OpCode_random:
case OpCode_mul:
case OpCode_sqrt:
case OpCode_dup:
case OpCode_exch:
case OpCode_index:
case OpCode_roll:
return true;
default:
return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
}
}
static inline bool is_subr_op (OpCode op)
{
switch (op)
{
case OpCode_callsubr:
case OpCode_callgsubr:
case OpCode_return:
return true;
default:
return false;
}
}
}; };
template <typename ENV, typename OPSET, typename PARAM> template <typename ENV, typename OPSET, typename PARAM>

View File

@ -38,6 +38,8 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs) inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs)
{ {
CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs); CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs);
processed_width = false;
has_width = false;
for (unsigned int i = 0; i < kTransientArraySize; i++) for (unsigned int i = 0; i < kTransientArraySize; i++)
transient_array[i].set_int (0); transient_array[i].set_int (0);
} }
@ -45,25 +47,29 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
bool check_transient_array_index (unsigned int i) const bool check_transient_array_index (unsigned int i) const
{ return i < kTransientArraySize; } { return i < kTransientArraySize; }
inline void process_width (void) inline void check_width (void)
{ {
if (!seen_width && (argStack.size > 0)) if (!processed_width)
{ {
assert (argStack.size == 1); if ((this->argStack.size & 1) != 0)
width = argStack.pop (); {
seen_width = true; width = this->argStack.elements[0];
has_width = true;
}
processed_width = true;
} }
} }
bool seen_width; bool processed_width;
bool has_width;
Number width; Number width;
static const unsigned int kTransientArraySize = 32; static const unsigned int kTransientArraySize = 32;
Number transient_array[kTransientArraySize]; Number transient_array[kTransientArraySize];
}; };
template <typename PARAM> template <typename OPSET, typename PARAM>
struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM> struct CFF1CSOpSet : CSOpSet<OPSET, CFF1CSInterpEnv, PARAM>
{ {
static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param) static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
{ {
@ -133,7 +139,7 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
break; break;
case OpCode_random: case OpCode_random:
if (unlikely (!env.argStack.check_overflow (1))) return false; if (unlikely (!env.argStack.check_overflow (1))) return false;
env.argStack.push_real (((float)rand() + 1) / ((float)RAND_MAX + 1)); env.argStack.push_int (1); /* we can't deal with random behavior; make it constant */
case OpCode_mul: case OpCode_mul:
if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false; if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
env.argStack.push_real (n1.to_real() * n2.to_real()); env.argStack.push_real (n1.to_real() * n2.to_real());
@ -181,14 +187,21 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
} }
break; break;
default: default:
typedef CSOpSet<CFF1Subrs, PARAM> SUPER;
if (unlikely (!SUPER::process_op (op, env, param))) if (unlikely (!SUPER::process_op (op, env, param)))
return false; return false;
env.process_width ();
break; break;
} }
return true; return true;
} }
static inline void flush_stack (CFF1CSInterpEnv &env, PARAM& param)
{
env.check_width ();
SUPER::flush_stack (env, param);
}
private:
typedef CSOpSet<OPSET, CFF1CSInterpEnv, PARAM> SUPER;
}; };
template <typename OPSET, typename PARAM> template <typename OPSET, typename PARAM>

View File

@ -43,11 +43,11 @@ struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
inline bool fetch_op (OpCode &op) inline bool fetch_op (OpCode &op)
{ {
if (unlikely (substr.avail ())) if (unlikely (this->substr.avail ()))
return CSInterpEnv<CFF2Subrs>::fetch_op (op); return CSInterpEnv<CFF2Subrs>::fetch_op (op);
/* make up return or endchar op */ /* make up return or endchar op */
if (callStack.check_underflow ()) if (this->callStack.check_underflow ())
op = OpCode_return; op = OpCode_return;
else else
op = OpCode_endchar; op = OpCode_endchar;
@ -61,26 +61,26 @@ struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
unsigned int ivs; unsigned int ivs;
}; };
template <typename PARAM> template <typename OPSET, typename PARAM>
struct CFF2CSOpSet : CSOpSet<CFF2Subrs, PARAM> struct CFF2CSOpSet : CSOpSet<OPSET, CFF2CSInterpEnv, PARAM>
{ {
static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param) static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
{ {
switch (op) { switch (op) {
case OpCode_blendcs: case OpCode_blendcs:
env.clear_stack (); // XXX: TODO //env.flush_stack (); // XXX: TODO
break; break;
case OpCode_vsindexcs: case OpCode_vsindexcs:
{ {
unsigned int ivs; unsigned int ivs;
if (unlikely (!env.argStack.check_pop_uint (ivs))) return false; if (unlikely (!env.argStack.check_pop_uint (ivs))) return false;
env.set_ivs (ivs); env.set_ivs (ivs);
env.clear_stack (); //env.flush_stack ();
} }
break; break;
default: default:
typedef CSOpSet<CFF2Subrs, PARAM> SUPER; typedef CSOpSet<OPSET, CFF2CSInterpEnv, PARAM> SUPER;
if (unlikely (!SUPER::process_op (op, env, param))) if (unlikely (!SUPER::process_op (op, env, param)))
return false; return false;
break; break;

View File

@ -299,12 +299,12 @@ struct Dict : UnsizedByteStr
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
/* serialize the opcode */ /* serialize the opcode */
HBUINT8 *p = c->allocate_size<HBUINT8> ((op >= OpCode_ESC_Base)? 2: 1); HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
if (unlikely (p == nullptr)) return_trace (false); if (unlikely (p == nullptr)) return_trace (false);
if (op >= OpCode_ESC_Base) if (Is_OpCode_ESC (op))
{ {
p->set (OpCode_escape); p->set (OpCode_escape);
op = (OpCode)(op - OpCode_ESC_Base); op = Unmake_OpCode_ESC (op);
p++; p++;
} }
p->set (op); p->set (op);
@ -344,7 +344,7 @@ struct FDMap : hb_vector_t<hb_codepoint_t>
inline bool fullset (void) const inline bool fullset (void) const
{ {
for (unsigned int i = 0; i < len; i++) for (unsigned int i = 0; i < len; i++)
if ((*this)[i] == HB_SET_VALUE_INVALID) if (hb_vector_t<hb_codepoint_t>::operator[] (i) == HB_SET_VALUE_INVALID)
return false; return false;
return true; return true;
} }
@ -574,7 +574,7 @@ struct Subrs : CFFIndex<COUNT>
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (&subrs == &Null(Subrs<COUNT>)) if (&subrs == &Null(Subrs<COUNT>))
return_trace (true); return_trace (true);
if ((subrs.count == 0) || (hb_set_get_population (set) == 0)) if ((subrs.count == 0) || (set == nullptr) || (hb_set_is_empty (set)))
{ {
if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size))) if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
return_trace (false); return_trace (false);

View File

@ -34,12 +34,108 @@
namespace CFF { namespace CFF {
/* Used for writing a temporary charstring */
struct ByteStrBuff : hb_vector_t<char, 1>
{
inline bool encode_byte (unsigned char b)
{
return (push ((const char)b) != &Crap(char));
}
inline bool encode_num (const Number& n)
{
if (n.in_int_range ())
{
int v = n.to_int ();
if ((-1131 <= v) && (v <= 1131))
{
if ((-107 <= v) && (v <= 107))
return encode_byte (v + 139);
else if (v > 0)
{
v -= 108;
return 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);
}
}
assert ((v & ~0xFFFF) == 0);
return encode_byte (OpCode_shortint) &&
encode_byte ((v >> 8) & 0xFF) &&
encode_byte (v & 0xFF);
}
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);
}
}
inline bool encode_op (OpCode op)
{
if (Is_OpCode_ESC (op))
return encode_byte (OpCode_escape) &&
encode_byte (Unmake_OpCode_ESC (op));
else
return encode_byte (op);
}
};
struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1>
{
inline void fini (void)
{
for (unsigned int i = 0; i < len; i++)
hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini ();
hb_vector_t<ByteStrBuff, 1>::fini ();
}
};
template <typename ACCESSOR, typename ENV, typename OPSET>
struct SubrFlattener
{
inline SubrFlattener (const ACCESSOR &acc_, const hb_vector_t<hb_codepoint_t> &glyphs_)
: acc (acc_),
glyphs (glyphs_)
{}
inline bool flatten (ByteStrBuffArray &flat_charstrings)
{
if (!flat_charstrings.resize (glyphs.len))
return false;
for (unsigned int i = 0; i < glyphs.len; i++)
flat_charstrings[i].init ();
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, ByteStrBuff> interp;
interp.env.init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
if (unlikely (!interp.interpret (flat_charstrings[i])))
return false;
}
return true;
}
const ACCESSOR &acc;
const hb_vector_t<hb_codepoint_t> &glyphs;
};
struct SubrRefMaps struct SubrRefMaps
{ {
inline SubrRefMaps (void) inline void init (void)
: valid (false),
global_map (nullptr)
{ {
valid = false;
global_map = nullptr;
local_maps.init (); local_maps.init ();
} }

View File

@ -167,49 +167,84 @@ struct CFF1FontDict_OpSerializer : OpSerializer
struct CFF1PrivateDict_OpSerializer : OpSerializer struct CFF1PrivateDict_OpSerializer : OpSerializer
{ {
inline CFF1PrivateDict_OpSerializer (bool drop_hints_=false, bool flatten_subrs_=false)
: drop_hints (drop_hints_), flatten_subrs (flatten_subrs_) {}
inline bool serialize (hb_serialize_context_t *c, inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr, const OpStr &opstr,
const unsigned int subrsOffset) const const unsigned int subrsOffset) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return true;
if (opstr.op == OpCode_Subrs) if (opstr.op == OpCode_Subrs)
return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset)); {
if (flatten_subrs)
return_trace (true);
else
return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
}
else else
return_trace (copy_opstr (c, opstr)); return_trace (copy_opstr (c, opstr));
} }
inline unsigned int calculate_serialized_size (const OpStr &opstr) const inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{ {
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return 0;
if (opstr.op == OpCode_Subrs) if (opstr.op == OpCode_Subrs)
return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); {
if (flatten_subrs)
return 0;
else
return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
}
else else
return opstr.str.len; return opstr.str.len;
} }
protected:
const bool drop_hints;
const bool flatten_subrs;
}; };
struct CFF1PrivateDict_OpSerializer_DropHints : CFF1PrivateDict_OpSerializer struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, ByteStrBuff>
{ {
inline bool serialize (hb_serialize_context_t *c, static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
const OpStr &opstr,
const unsigned int subrsOffset) const
{ {
if (DictOpSet::is_hint_op (opstr.op)) if (unlikely (!SUPER::process_op (op, env, flatStr)))
return true; return false;
else switch (op)
return CFF1PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset); {
case OpCode_hintmask:
case OpCode_cntrmask:
if (unlikely (!flatStr.encode_op (op)))
return false;
for (int i = -env.hintmask_size; i < 0; i++)
if (unlikely (!flatStr.encode_byte (env.substr[i])))
return false;
break;
default:
if (!CSOpSet::is_subr_op (op) &&
!CSOpSet::is_arg_op (op))
return flatStr.encode_op (op);
}
return true;
} }
inline unsigned int calculate_serialized_size (const OpStr &opstr) const static inline void flush_stack (CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
{ {
if (DictOpSet::is_hint_op (opstr.op)) for (unsigned int i = 0; i < env.argStack.size; i++)
return 0; flatStr.encode_num (env.argStack.elements[i]);
else SUPER::flush_stack (env, flatStr);
return CFF1PrivateDict_OpSerializer::calculate_serialized_size (opstr);
} }
private:
typedef CFF1CSOpSet<CFF1CSOpSet_Flatten, ByteStrBuff> SUPER;
}; };
struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<SubrRefMapPair> struct CFF1CSOpSet_SubsetSubrs : CFF1CSOpSet<CFF1CSOpSet_SubsetSubrs, SubrRefMapPair>
{ {
static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, SubrRefMapPair& refMapPair) static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, SubrRefMapPair& refMapPair)
{ {
@ -230,7 +265,7 @@ struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<SubrRefMapPair>
default: default:
break; break;
} }
return CFF1CSOpSet<SubrRefMapPair>::process_op (op, env, refMapPair); return CFF1CSOpSet<CFF1CSOpSet_SubsetSubrs, SubrRefMapPair>::process_op (op, env, refMapPair);
} }
}; };
@ -238,16 +273,20 @@ struct cff_subset_plan {
inline cff_subset_plan (void) inline cff_subset_plan (void)
: final_size (0), : final_size (0),
orig_fdcount (0), orig_fdcount (0),
subst_fdcount(1), subst_fdcount (1),
subst_fdselect_format (0), subst_fdselect_format (0),
offsets() offsets (),
flatten_subrs (true),
drop_hints (false)
{ {
topdict_sizes.init (); topdict_sizes.init ();
topdict_sizes.resize (1); topdict_sizes.resize (1);
subst_fdselect_first_glyphs.init (); subst_fdselect_first_glyphs.init ();
fdmap.init (); fdmap.init ();
subset_charstrings.init (); subset_charstrings.init ();
flat_charstrings.init ();
privateDictInfos.init (); privateDictInfos.init ();
subrRefMaps.init ();
} }
inline ~cff_subset_plan (void) inline ~cff_subset_plan (void)
@ -256,6 +295,7 @@ struct cff_subset_plan {
subst_fdselect_first_glyphs.fini (); subst_fdselect_first_glyphs.fini ();
fdmap.fini (); fdmap.fini ();
subset_charstrings.fini (); subset_charstrings.fini ();
flat_charstrings.fini ();
privateDictInfos.fini (); privateDictInfos.fini ();
subrRefMaps.fini (); subrRefMaps.fini ();
} }
@ -287,9 +327,20 @@ struct cff_subset_plan {
offsets.stringIndexOffset = final_size; offsets.stringIndexOffset = final_size;
final_size += acc.stringIndex->get_size (); final_size += acc.stringIndex->get_size ();
/* Subset global & local subrs */ if (flatten_subrs)
{ {
SubrSubsetter<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubrSubset> subsetter(acc, plan->glyphs); /* Flatten global & local subrs */
SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten> flattener(acc, plan->glyphs);
if (!flattener.flatten (flat_charstrings))
return false;
/* no global/local subroutines */
offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */
}
else
{
/* Subset global & local subrs */
SubrSubsetter<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubsetSubrs> subsetter(acc, plan->glyphs);
if (!subsetter.collect_refs (subrRefMaps)) if (!subsetter.collect_refs (subrRefMaps))
return false; return false;
@ -299,7 +350,6 @@ struct cff_subset_plan {
for (unsigned int i = 0; i < orig_fdcount; i++) 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], 1); offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i], 1);
} }
/* global subrs */ /* global subrs */
offsets.globalSubrsInfo.offset = final_size; offsets.globalSubrsInfo.offset = final_size;
final_size += offsets.globalSubrsInfo.size; final_size += offsets.globalSubrsInfo.size;
@ -346,9 +396,19 @@ struct cff_subset_plan {
unsigned int dataSize = 0; unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++) for (unsigned int i = 0; i < plan->glyphs.len; i++)
{ {
const ByteStr str = (*acc.charStrings)[plan->glyphs[i]]; if (flatten_subrs)
subset_charstrings.push (str); {
dataSize += str.len; ByteStrBuff &flatstr = flat_charstrings[i];
ByteStr str (&flatstr[0], flatstr.len);
subset_charstrings.push (str);
dataSize += flatstr.len;
}
else
{
const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
subset_charstrings.push (str);
dataSize += str.len;
}
} }
offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1); offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize); final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
@ -361,26 +421,21 @@ struct cff_subset_plan {
if (!fdmap.excludes (i)) if (!fdmap.excludes (i))
{ {
unsigned int priv_size; unsigned int priv_size;
if (plan->drop_hints) CFF1PrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs);
{ priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
CFF1PrivateDict_OpSerializer_DropHints privSzr_drop;
priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr_drop);
}
else
{
CFF1PrivateDict_OpSerializer privSzr;
priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
}
TableInfo privInfo = { final_size, priv_size, 0 }; TableInfo privInfo = { final_size, priv_size, 0 };
privateDictInfos.push (privInfo); privateDictInfos.push (privInfo);
final_size += privInfo.size + offsets.localSubrsInfos[i].size; final_size += privInfo.size;
if (!flatten_subrs)
final_size += offsets.localSubrsInfos[i].size;
} }
} }
if (!acc.is_CID ()) if (!acc.is_CID ())
offsets.privateDictInfo = privateDictInfos[0]; offsets.privateDictInfo = privateDictInfos[0];
return true; return ((subset_charstrings.len == plan->glyphs.len) &&
(privateDictInfos.len == subst_fdcount));
} }
inline unsigned int get_final_size (void) const { return final_size; } inline unsigned int get_final_size (void) const { return final_size; }
@ -399,11 +454,13 @@ struct cff_subset_plan {
* set to HB_SET_VALUE_INVALID if excluded from subset */ * set to HB_SET_VALUE_INVALID if excluded from subset */
FDMap fdmap; FDMap fdmap;
hb_vector_t<ByteStr> subset_charstrings; hb_vector_t<ByteStr> subset_charstrings;
hb_vector_t<TableInfo> privateDictInfos; ByteStrBuffArray flat_charstrings;
hb_vector_t<TableInfo> privateDictInfos;
SubrRefMaps subrRefMaps; SubrRefMaps subrRefMaps;
bool flatten_subrs;
bool drop_hints; bool drop_hints;
}; };
@ -467,6 +524,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
/* global subrs */ /* global subrs */
{ {
assert (plan.offsets.globalSubrsInfo.offset != 0);
assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start); assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
CFF1Subrs *dest = c.start_embed<CFF1Subrs> (); CFF1Subrs *dest = c.start_embed<CFF1Subrs> ();
if (unlikely (dest == nullptr)) return false; if (unlikely (dest == nullptr)) return false;
@ -565,25 +623,17 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
{ {
PrivateDict *pd = c.start_embed<PrivateDict> (); PrivateDict *pd = c.start_embed<PrivateDict> ();
if (unlikely (pd == nullptr)) return false; if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; unsigned int priv_size = plan.flatten_subrs? 0: plan.privateDictInfos[plan.fdmap[i]].size;
bool result; bool result;
CFF1PrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
if (plan.drop_hints) result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
{
CFF1PrivateDict_OpSerializer_DropHints privSzr_drop;
result = pd->serialize (&c, acc.privateDicts[i], privSzr_drop, priv_size);
}
else
{
CFF1PrivateDict_OpSerializer privSzr;
result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
}
if (unlikely (!result)) if (unlikely (!result))
{ {
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
return false; return false;
} }
if (acc.privateDicts[i].subrsOffset != 0) if (!plan.flatten_subrs && (acc.privateDicts[i].subrsOffset != 0))
{ {
CFF1Subrs *subrs = c.start_embed<CFF1Subrs> (); CFF1Subrs *subrs = c.start_embed<CFF1Subrs> ();
if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF1Subrs)) if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF1Subrs))

View File

@ -187,7 +187,7 @@ struct CFF2PrivateDict_OpSerializer_DropHints : CFF2PrivateDict_OpSerializer
} }
}; };
struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<SubrRefMapPair> struct CFF2CSOpSet_SubsetSubrs : CFF2CSOpSet<CFF2CSOpSet_SubsetSubrs, SubrRefMapPair>
{ {
static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair) static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair)
{ {
@ -208,7 +208,7 @@ struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<SubrRefMapPair>
default: default:
break; break;
} }
return CFF2CSOpSet<SubrRefMapPair>::process_op (op, env, refMapPair); return CFF2CSOpSet<CFF2CSOpSet_SubsetSubrs, SubrRefMapPair>::process_op (op, env, refMapPair);
} }
}; };
@ -223,6 +223,7 @@ struct cff2_subset_plan {
fdmap.init (); fdmap.init ();
subset_charstrings.init (); subset_charstrings.init ();
privateDictInfos.init (); privateDictInfos.init ();
subrRefMaps.init ();
} }
inline ~cff2_subset_plan (void) inline ~cff2_subset_plan (void)
@ -254,7 +255,7 @@ struct cff2_subset_plan {
/* Subset global & local subrs */ /* Subset global & local subrs */
{ {
SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset> subsetter(acc, plan->glyphs); SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubsetSubrs> subsetter(acc, plan->glyphs);
if (!subsetter.collect_refs (subrRefMaps)) if (!subsetter.collect_refs (subrRefMaps))
return false; return false;