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 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];
}

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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);

View File

@ -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 ();
}

View File

@ -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)
{
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)
{
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))
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;
else
return CFF1PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset);
}
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;
@ -345,11 +395,21 @@ struct cff_subset_plan {
offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++)
{
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;
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; }
@ -400,10 +455,12 @@ struct cff_subset_plan {
FDMap fdmap;
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);
}
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))

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)
{
@ -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;