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:
parent
27c32d8afb
commit
a11420b48c
|
@ -204,23 +204,62 @@ enum OpCode {
|
|||
};
|
||||
|
||||
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
|
||||
{
|
||||
inline Number (void) { set_int (0); }
|
||||
|
||||
inline void set_int (int v) { is_real = false; u.int_val = v; };
|
||||
inline int to_int (void) const { return is_real? (int)u.real_val: u.int_val; }
|
||||
inline void set_real (float v) { is_real = true; u.real_val = v; };
|
||||
inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; }
|
||||
inline void set_int (int v) { format = NumInt; u.int_val = v; };
|
||||
inline int to_int (void) const { return is_int ()? u.int_val: (int)to_real (); }
|
||||
inline void set_fixed (int32_t v) { format = NumFixed; u.fixed_val = v; };
|
||||
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:
|
||||
bool is_real;
|
||||
enum NumFormat {
|
||||
NumInt,
|
||||
NumFixed,
|
||||
NumReal
|
||||
};
|
||||
NumFormat format;
|
||||
union {
|
||||
int int_val;
|
||||
int32_t fixed_val;
|
||||
float real_val;
|
||||
} 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 */
|
||||
|
@ -308,7 +347,7 @@ struct SubByteStr
|
|||
offset = offset_;
|
||||
}
|
||||
|
||||
inline const HBUINT8& operator [] (unsigned int i) const {
|
||||
inline const HBUINT8& operator [] (int i) const {
|
||||
return str[offset + i];
|
||||
}
|
||||
|
||||
|
|
|
@ -125,18 +125,6 @@ struct CSInterpEnv : InterpEnv
|
|||
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)
|
||||
|
@ -149,13 +137,12 @@ struct CSInterpEnv : InterpEnv
|
|||
inline bool is_endchar (void) const { return endchar_flag; }
|
||||
inline bool is_stack_cleared (void) const { return stack_cleared; }
|
||||
|
||||
protected:
|
||||
public:
|
||||
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;
|
||||
|
@ -164,10 +151,10 @@ struct CSInterpEnv : InterpEnv
|
|||
BiasedSubrs<SUBRS> localSubrs;
|
||||
};
|
||||
|
||||
template <typename SUBRS, typename PARAM>
|
||||
template <typename OPSET, typename ENV, typename PARAM>
|
||||
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) {
|
||||
|
||||
|
@ -188,17 +175,16 @@ struct CSOpSet : OpSet
|
|||
|
||||
case OpCode_hstem:
|
||||
case OpCode_hstemhm:
|
||||
env.hstem_count += env.argStack.size / 2;
|
||||
env.clear_stack ();
|
||||
OPSET::process_hstem (env, param);
|
||||
break;
|
||||
case OpCode_vstem:
|
||||
case OpCode_vstemhm:
|
||||
env.vstem_count += env.argStack.size / 2;
|
||||
env.clear_stack ();
|
||||
OPSET::process_vstem (env, param);
|
||||
break;
|
||||
case OpCode_hintmask:
|
||||
case OpCode_cntrmask:
|
||||
env.determine_hintmask_size ();
|
||||
OPSET::flush_stack (env, param);
|
||||
if (unlikely (!env.substr.avail (env.hintmask_size)))
|
||||
return false;
|
||||
env.substr.inc (env.hintmask_size);
|
||||
|
@ -210,7 +196,7 @@ struct CSOpSet : OpSet
|
|||
case OpCode_vlineto:
|
||||
case OpCode_rmoveto:
|
||||
case OpCode_hmoveto:
|
||||
env.process_moveto ();
|
||||
OPSET::process_moveto (env, param);
|
||||
break;
|
||||
case OpCode_rrcurveto:
|
||||
case OpCode_rcurveline:
|
||||
|
@ -223,7 +209,7 @@ struct CSOpSet : OpSet
|
|||
case OpCode_flex:
|
||||
case OpCode_hflex1:
|
||||
case OpCode_flex1:
|
||||
env.clear_stack ();
|
||||
OPSET::flush_stack (env, param);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -231,6 +217,83 @@ struct CSOpSet : OpSet
|
|||
}
|
||||
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>
|
||||
|
|
|
@ -38,6 +38,8 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
|
|||
inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs)
|
||||
{
|
||||
CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs);
|
||||
processed_width = false;
|
||||
has_width = false;
|
||||
for (unsigned int i = 0; i < kTransientArraySize; i++)
|
||||
transient_array[i].set_int (0);
|
||||
}
|
||||
|
@ -45,25 +47,29 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
|
|||
bool check_transient_array_index (unsigned int i) const
|
||||
{ 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);
|
||||
width = argStack.pop ();
|
||||
seen_width = true;
|
||||
if ((this->argStack.size & 1) != 0)
|
||||
{
|
||||
width = this->argStack.elements[0];
|
||||
has_width = true;
|
||||
}
|
||||
processed_width = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool seen_width;
|
||||
bool processed_width;
|
||||
bool has_width;
|
||||
Number width;
|
||||
|
||||
static const unsigned int kTransientArraySize = 32;
|
||||
Number transient_array[kTransientArraySize];
|
||||
};
|
||||
|
||||
template <typename PARAM>
|
||||
struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
|
||||
template <typename OPSET, typename PARAM>
|
||||
struct CFF1CSOpSet : CSOpSet<OPSET, CFF1CSInterpEnv, PARAM>
|
||||
{
|
||||
static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
|
||||
{
|
||||
|
@ -133,7 +139,7 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
|
|||
break;
|
||||
case OpCode_random:
|
||||
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:
|
||||
if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
|
||||
env.argStack.push_real (n1.to_real() * n2.to_real());
|
||||
|
@ -181,14 +187,21 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
|
|||
}
|
||||
break;
|
||||
default:
|
||||
typedef CSOpSet<CFF1Subrs, PARAM> SUPER;
|
||||
if (unlikely (!SUPER::process_op (op, env, param)))
|
||||
return false;
|
||||
env.process_width ();
|
||||
break;
|
||||
}
|
||||
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>
|
||||
|
|
|
@ -43,11 +43,11 @@ struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
|
|||
|
||||
inline bool fetch_op (OpCode &op)
|
||||
{
|
||||
if (unlikely (substr.avail ()))
|
||||
if (unlikely (this->substr.avail ()))
|
||||
return CSInterpEnv<CFF2Subrs>::fetch_op (op);
|
||||
|
||||
/* make up return or endchar op */
|
||||
if (callStack.check_underflow ())
|
||||
if (this->callStack.check_underflow ())
|
||||
op = OpCode_return;
|
||||
else
|
||||
op = OpCode_endchar;
|
||||
|
@ -61,26 +61,26 @@ struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
|
|||
unsigned int ivs;
|
||||
};
|
||||
|
||||
template <typename PARAM>
|
||||
struct CFF2CSOpSet : CSOpSet<CFF2Subrs, PARAM>
|
||||
template <typename OPSET, typename PARAM>
|
||||
struct CFF2CSOpSet : CSOpSet<OPSET, CFF2CSInterpEnv, PARAM>
|
||||
{
|
||||
static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
|
||||
{
|
||||
switch (op) {
|
||||
|
||||
case OpCode_blendcs:
|
||||
env.clear_stack (); // XXX: TODO
|
||||
//env.flush_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 ();
|
||||
//env.flush_stack ();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
typedef CSOpSet<CFF2Subrs, PARAM> SUPER;
|
||||
typedef CSOpSet<OPSET, CFF2CSInterpEnv, PARAM> SUPER;
|
||||
if (unlikely (!SUPER::process_op (op, env, param)))
|
||||
return false;
|
||||
break;
|
||||
|
|
|
@ -299,12 +299,12 @@ struct Dict : UnsizedByteStr
|
|||
|
||||
TRACE_SERIALIZE (this);
|
||||
/* 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 (op >= OpCode_ESC_Base)
|
||||
if (Is_OpCode_ESC (op))
|
||||
{
|
||||
p->set (OpCode_escape);
|
||||
op = (OpCode)(op - OpCode_ESC_Base);
|
||||
op = Unmake_OpCode_ESC (op);
|
||||
p++;
|
||||
}
|
||||
p->set (op);
|
||||
|
@ -344,7 +344,7 @@ struct FDMap : hb_vector_t<hb_codepoint_t>
|
|||
inline bool fullset (void) const
|
||||
{
|
||||
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 true;
|
||||
}
|
||||
|
@ -574,7 +574,7 @@ struct Subrs : CFFIndex<COUNT>
|
|||
TRACE_SERIALIZE (this);
|
||||
if (&subrs == &Null(Subrs<COUNT>))
|
||||
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)))
|
||||
return_trace (false);
|
||||
|
|
|
@ -34,12 +34,108 @@
|
|||
|
||||
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
|
||||
{
|
||||
inline SubrRefMaps (void)
|
||||
: valid (false),
|
||||
global_map (nullptr)
|
||||
inline void init (void)
|
||||
{
|
||||
valid = false;
|
||||
global_map = nullptr;
|
||||
local_maps.init ();
|
||||
}
|
||||
|
||||
|
|
|
@ -167,49 +167,84 @@ struct CFF1FontDict_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,
|
||||
const OpStr &opstr,
|
||||
const unsigned int subrsOffset) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
|
||||
return true;
|
||||
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
|
||||
return_trace (copy_opstr (c, opstr));
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
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,
|
||||
const OpStr &opstr,
|
||||
const unsigned int subrsOffset) const
|
||||
static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
|
||||
{
|
||||
if (DictOpSet::is_hint_op (opstr.op))
|
||||
return true;
|
||||
else
|
||||
return CFF1PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset);
|
||||
if (unlikely (!SUPER::process_op (op, env, flatStr)))
|
||||
return false;
|
||||
switch (op)
|
||||
{
|
||||
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))
|
||||
return 0;
|
||||
else
|
||||
return CFF1PrivateDict_OpSerializer::calculate_serialized_size (opstr);
|
||||
for (unsigned int i = 0; i < env.argStack.size; i++)
|
||||
flatStr.encode_num (env.argStack.elements[i]);
|
||||
SUPER::flush_stack (env, flatStr);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -230,7 +265,7 @@ struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<SubrRefMapPair>
|
|||
default:
|
||||
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)
|
||||
: final_size (0),
|
||||
orig_fdcount (0),
|
||||
subst_fdcount(1),
|
||||
subst_fdcount (1),
|
||||
subst_fdselect_format (0),
|
||||
offsets()
|
||||
offsets (),
|
||||
flatten_subrs (true),
|
||||
drop_hints (false)
|
||||
{
|
||||
topdict_sizes.init ();
|
||||
topdict_sizes.resize (1);
|
||||
subst_fdselect_first_glyphs.init ();
|
||||
fdmap.init ();
|
||||
subset_charstrings.init ();
|
||||
flat_charstrings.init ();
|
||||
privateDictInfos.init ();
|
||||
subrRefMaps.init ();
|
||||
}
|
||||
|
||||
inline ~cff_subset_plan (void)
|
||||
|
@ -256,6 +295,7 @@ struct cff_subset_plan {
|
|||
subst_fdselect_first_glyphs.fini ();
|
||||
fdmap.fini ();
|
||||
subset_charstrings.fini ();
|
||||
flat_charstrings.fini ();
|
||||
privateDictInfos.fini ();
|
||||
subrRefMaps.fini ();
|
||||
}
|
||||
|
@ -287,9 +327,20 @@ struct cff_subset_plan {
|
|||
offsets.stringIndexOffset = final_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))
|
||||
return false;
|
||||
|
||||
|
@ -299,7 +350,6 @@ struct cff_subset_plan {
|
|||
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);
|
||||
}
|
||||
|
||||
/* global subrs */
|
||||
offsets.globalSubrsInfo.offset = final_size;
|
||||
final_size += offsets.globalSubrsInfo.size;
|
||||
|
@ -346,9 +396,19 @@ struct cff_subset_plan {
|
|||
unsigned int dataSize = 0;
|
||||
for (unsigned int i = 0; i < plan->glyphs.len; i++)
|
||||
{
|
||||
const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
|
||||
subset_charstrings.push (str);
|
||||
dataSize += str.len;
|
||||
if (flatten_subrs)
|
||||
{
|
||||
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);
|
||||
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))
|
||||
{
|
||||
unsigned int priv_size;
|
||||
if (plan->drop_hints)
|
||||
{
|
||||
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);
|
||||
}
|
||||
CFF1PrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs);
|
||||
priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
|
||||
TableInfo privInfo = { final_size, priv_size, 0 };
|
||||
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 ())
|
||||
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; }
|
||||
|
@ -399,11 +454,13 @@ struct cff_subset_plan {
|
|||
* set to HB_SET_VALUE_INVALID if excluded from subset */
|
||||
FDMap fdmap;
|
||||
|
||||
hb_vector_t<ByteStr> subset_charstrings;
|
||||
hb_vector_t<TableInfo> privateDictInfos;
|
||||
hb_vector_t<ByteStr> subset_charstrings;
|
||||
ByteStrBuffArray flat_charstrings;
|
||||
hb_vector_t<TableInfo> privateDictInfos;
|
||||
|
||||
SubrRefMaps subrRefMaps;
|
||||
|
||||
bool flatten_subrs;
|
||||
bool drop_hints;
|
||||
};
|
||||
|
||||
|
@ -467,6 +524,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
|
|||
|
||||
/* global subrs */
|
||||
{
|
||||
assert (plan.offsets.globalSubrsInfo.offset != 0);
|
||||
assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
|
||||
CFF1Subrs *dest = c.start_embed<CFF1Subrs> ();
|
||||
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> ();
|
||||
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;
|
||||
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 */
|
||||
if (plan.drop_hints)
|
||||
{
|
||||
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);
|
||||
}
|
||||
result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
|
||||
if (unlikely (!result))
|
||||
{
|
||||
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
|
||||
return false;
|
||||
}
|
||||
if (acc.privateDicts[i].subrsOffset != 0)
|
||||
if (!plan.flatten_subrs && (acc.privateDicts[i].subrsOffset != 0))
|
||||
{
|
||||
CFF1Subrs *subrs = c.start_embed<CFF1Subrs> ();
|
||||
if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF1Subrs))
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -208,7 +208,7 @@ struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<SubrRefMapPair>
|
|||
default:
|
||||
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 ();
|
||||
subset_charstrings.init ();
|
||||
privateDictInfos.init ();
|
||||
subrRefMaps.init ();
|
||||
}
|
||||
|
||||
inline ~cff2_subset_plan (void)
|
||||
|
@ -254,7 +255,7 @@ struct cff2_subset_plan {
|
|||
|
||||
/* 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))
|
||||
return false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue