CFF1 no-desubroutinize + no-hinting

no-desubroutinize option is disabled for now
code cleanup (esp. CFF1 width handling)
bug fixes & renaming
This commit is contained in:
Michiharu Ariza 2018-10-31 22:30:34 -07:00
parent be746009e9
commit d56e338a90
16 changed files with 1130 additions and 250 deletions

View File

@ -448,6 +448,8 @@ struct SubByteStr
bool error;
};
typedef hb_vector_t<ByteStr> ByteStrArray;
/* stack */
template <typename ELEM, int LIMIT>
struct Stack
@ -652,6 +654,53 @@ struct OpSerializer
}
};
template <typename VAL>
struct ParsedValues
{
inline void init (void)
{
opStart = 0;
values.init ();
}
inline void fini (void)
{
values.fini_deep ();
}
inline void add_op (OpCode op, const SubByteStr& substr = SubByteStr ())
{
VAL *val = values.push ();
val->op = op;
assert (substr.offset >= opStart);
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline void add_op (OpCode op, const SubByteStr& substr, const VAL &v)
{
VAL *val = values.push (v);
val->op = op;
assert (substr.offset >= opStart);
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline bool has_op (OpCode op) const
{
for (unsigned int i = 0; i < get_count (); i++)
if (get_value (i).op == op) return true;
return false;
}
inline unsigned get_count (void) const { return values.len; }
inline const VAL &get_value (unsigned int i) const { return values[i]; }
inline const VAL &operator [] (unsigned int i) const { return get_value (i); }
unsigned int opStart;
hb_vector_t<VAL> values;
};
template <typename ARG=Number>
struct InterpEnv
{

View File

@ -33,8 +33,31 @@ namespace CFF {
using namespace OT;
enum CSType {
CSType_CharString,
CSType_GlobalSubr,
CSType_LocalSubr
};
struct CallContext
{
inline void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
{
substr = substr_;
type = type_;
subr_num = subr_num_;
}
inline void fini (void) {}
SubByteStr substr;
CSType type;
unsigned int subr_num;
};
/* call stack */
struct CallStack : Stack<SubByteStr, 10> {};
const unsigned int kMaxCallLimit = 10;
struct CallStack : Stack<CallContext, kMaxCallLimit> {};
template <typename SUBRS>
struct BiasedSubrs
@ -80,6 +103,7 @@ struct CSInterpEnv : InterpEnv<ARG>
{
InterpEnv<ARG>::init (str);
context.init (str, CSType_CharString);
seen_moveto = true;
seen_hintmask = false;
hstem_count = 0;
@ -114,23 +138,29 @@ struct CSInterpEnv : InterpEnv<ARG>
return true;
}
inline bool callSubr (const BiasedSubrs<SUBRS>& biasedSubrs)
inline void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
{
unsigned int subr_num;
if (unlikely (!popSubrNum (biasedSubrs, subr_num)))
return false;
callStack.push (SUPER::substr);
SUPER::substr = (*biasedSubrs.subrs)[subr_num];
if (unlikely (!popSubrNum (biasedSubrs, subr_num)
|| callStack.get_count () >= kMaxCallLimit))
{
SUPER::set_error ();
return;
}
context.substr = SUPER::substr;
callStack.push (context);
return true;
context.init ( (*biasedSubrs.subrs)[subr_num], type, subr_num);
SUPER::substr = context.substr;
}
inline void returnFromSubr (void)
{
if (unlikely (SUPER::substr.in_error ()))
SUPER::set_error ();
SUPER::substr = callStack.pop ();
context = callStack.pop ();
SUPER::substr = context.substr;
}
inline void determine_hintmask_size (void)
@ -153,6 +183,7 @@ struct CSInterpEnv : InterpEnv<ARG>
inline void moveto (const Point &pt_ ) { pt = pt_; }
public:
CallContext context;
bool endchar_flag;
bool seen_moveto;
bool seen_hintmask;
@ -206,6 +237,7 @@ struct CSOpSet : OpSet<ARG>
env.returnFromSubr ();
break;
case OpCode_endchar:
OPSET::check_width (op, env, param);
env.set_endchar (true);
OPSET::flush_args_and_op (op, env, param);
break;
@ -215,36 +247,42 @@ struct CSOpSet : OpSet<ARG>
break;
case OpCode_callsubr:
env.callSubr (env.localSubrs);
env.callSubr (env.localSubrs, CSType_LocalSubr);
break;
case OpCode_callgsubr:
env.callSubr (env.globalSubrs);
env.callSubr (env.globalSubrs, CSType_GlobalSubr);
break;
case OpCode_hstem:
case OpCode_hstemhm:
OPSET::check_width (op, env, param);
OPSET::process_hstem (op, env, param);
break;
case OpCode_vstem:
case OpCode_vstemhm:
OPSET::check_width (op, env, param);
OPSET::process_vstem (op, env, param);
break;
case OpCode_hintmask:
case OpCode_cntrmask:
OPSET::check_width (op, env, param);
OPSET::process_hintmask (op, env, param);
break;
case OpCode_rmoveto:
OPSET::check_width (op, env, param);
PATH::rmoveto (env, param);
process_post_move (op, env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_hmoveto:
OPSET::check_width (op, env, param);
PATH::hmoveto (env, param);
process_post_move (op, env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_vmoveto:
OPSET::check_width (op, env, param);
PATH::vmoveto (env, param);
process_post_move (op, env, param);
OPSET::process_post_move (op, env, param);
break;
case OpCode_rlineto:
PATH::rlineto (env, param);
@ -340,6 +378,9 @@ struct CSOpSet : OpSet<ARG>
OPSET::flush_args_and_op (op, env, param);
}
static inline void check_width (OpCode op, ENV &env, PARAM& param)
{}
static inline void process_post_move (OpCode op, ENV &env, PARAM& param)
{
if (!env.seen_moveto)
@ -355,15 +396,15 @@ struct CSOpSet : OpSet<ARG>
OPSET::flush_args_and_op (op, env, param);
}
static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param, unsigned int start_arg = 0)
static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
{
OPSET::flush_args (env, param, start_arg);
OPSET::flush_args (env, param);
OPSET::flush_op (op, env, param);
}
static inline void flush_args (ENV &env, PARAM& param, unsigned int start_arg = 0)
static inline void flush_args (ENV &env, PARAM& param)
{
env.pop_n_args (env.argStack.get_count () - start_arg);
env.pop_n_args (env.argStack.get_count ());
}
static inline void flush_op (OpCode op, ENV &env, PARAM& param)
@ -375,6 +416,24 @@ struct CSOpSet : OpSet<ARG>
OPSET::flush_args_and_op (op, env, param);
}
static inline bool is_number_op (OpCode op)
{
switch (op)
{
case OpCode_shortint:
case OpCode_fixedcs:
case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
return true;
default:
/* 1-byte integer */
return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
}
}
protected:
typedef OpSet<ARG> SUPER;
};

View File

@ -50,50 +50,7 @@ struct DictVal : OpStr
typedef DictVal NumDictVal;
template <typename VAL>
struct DictValues
{
inline void init (void)
{
opStart = 0;
values.init ();
}
inline void fini (void)
{
values.fini_deep ();
}
inline void addOp (OpCode op, const SubByteStr& substr = SubByteStr ())
{
VAL *val = values.push ();
val->op = op;
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline void addOp (OpCode op, const SubByteStr& substr, const VAL &v)
{
VAL *val = values.push (v);
val->op = op;
val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
opStart = substr.offset;
}
inline bool hasOp (OpCode op) const
{
for (unsigned int i = 0; i < getNumValues (); i++)
if (getValue (i).op == op) return true;
return false;
}
inline unsigned getNumValues (void) const { return values.len; }
inline const VAL &getValue (unsigned int i) const { return values[i]; }
inline const VAL &operator [] (unsigned int i) const { return getValue (i); }
unsigned int opStart;
hb_vector_t<VAL> values;
};
template <typename VAL> struct DictValues : ParsedValues<VAL> {};
template <typename OPSTR=OpStr>
struct TopDictValues : DictValues<OPSTR>

View File

@ -33,6 +33,8 @@ namespace CFF {
using namespace OT;
typedef BiasedSubrs<CFF1Subrs> CFF1BiasedSubrs;
struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
{
template <typename ACC>
@ -41,6 +43,7 @@ struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
processed_width = false;
has_width = false;
arg_start = 0;
}
inline void fini (void)
@ -48,24 +51,26 @@ struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
SUPER::fini ();
}
inline unsigned int check_width (void)
inline void set_width (void)
{
unsigned int arg_start = 0;
if (!processed_width)
if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
{
if ((SUPER::argStack.get_count () & 1) != 0)
{
width = SUPER::argStack[0];
has_width = true;
arg_start = 1;
}
width = SUPER::argStack[0];
has_width = true;
processed_width = true;
arg_start = 1;
}
return arg_start;
}
inline void clear_args (void)
{
arg_start = 0;
SUPER::clear_args ();
}
bool processed_width;
bool has_width;
unsigned int arg_start;
Number width;
private:
@ -77,10 +82,45 @@ struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
{
/* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param, unsigned int start_arg = 0)
static inline void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
{
start_arg = env.check_width ();
SUPER::flush_args (env, param, start_arg);
if (!env.processed_width)
{
bool has_width = false;
switch (op)
{
default:
case OpCode_endchar:
has_width = (env.argStack.get_count () > 0);
break;
case OpCode_hstem:
case OpCode_hstemhm:
case OpCode_hintmask:
case OpCode_cntrmask:
has_width = ((env.argStack.get_count () & 1) != 0);
break;
case OpCode_hmoveto:
case OpCode_vmoveto:
has_width = (env.argStack.get_count () > 1);
break;
case OpCode_rmoveto:
has_width = (env.argStack.get_count () > 2);
break;
}
if (has_width)
{
env.set_width ();
OPSET::process_width (env, param);
}
}
}
static inline void process_width (CFF1CSInterpEnv &env, PARAM& param)
{}
static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param)
{
SUPER::flush_args (env, param);
env.clear_args (); /* pop off width */
}

View File

@ -74,6 +74,7 @@ struct BlendArg : Number
};
typedef InterpEnv<BlendArg> BlendInterpEnv;
typedef BiasedSubrs<CFF2Subrs> CFF2BiasedSubrs;
struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
{

View File

@ -61,6 +61,18 @@ struct code_pair
hb_codepoint_t glyph;
};
typedef hb_vector_t<char, 1> StrBuff;
struct StrBuffArray : hb_vector_t<StrBuff>
{
inline unsigned int total_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < len; i++)
size += (*this)[i].len;
return size;
}
};
/* CFF INDEX */
template <typename COUNT>
struct CFFIndex
@ -95,7 +107,7 @@ struct CFFIndex
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const hb_vector_t<ByteStr> &byteArray)
const ByteStrArray &byteArray)
{
TRACE_SERIALIZE (this);
/* serialize CFFIndex header */
@ -126,6 +138,22 @@ struct CFFIndex
return_trace (true);
}
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const StrBuffArray &buffArray)
{
ByteStrArray byteArray;
byteArray.init ();
byteArray.resize (buffArray.len);
for (unsigned int i = 0; i < byteArray.len; i++)
{
byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len);
}
bool result = this->serialize (c, offSize_, byteArray);
byteArray.fini ();
return result;
}
inline void set_offset_at (unsigned int index, unsigned int offset)
{
HBUINT8 *p = offsets + offSize * index + offSize;
@ -280,7 +308,7 @@ struct Dict : UnsizedByteStr
PARAM& param)
{
TRACE_SERIALIZE (this);
for (unsigned int i = 0; i < dictval.getNumValues (); i++)
for (unsigned int i = 0; i < dictval.get_count (); i++)
{
if (unlikely (!opszr.serialize (c, dictval[i], param)))
return_trace (false);
@ -294,7 +322,7 @@ struct Dict : UnsizedByteStr
OP_SERIALIZER& opszr)
{
unsigned int size = 0;
for (unsigned int i = 0; i < dictval.getNumValues (); i++)
for (unsigned int i = 0; i < dictval.get_count (); i++)
size += opszr.calculate_serialized_size (dictval[i]);
return size;
}
@ -383,6 +411,9 @@ struct Remap : hb_vector_t<hb_codepoint_t>
inline bool excludes (hb_codepoint_t id) const
{ return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
inline bool includes (hb_codepoint_t id) const
{ return !excludes (id); }
inline hb_codepoint_t operator[] (hb_codepoint_t i) const
{
if (fullset ())
@ -649,52 +680,8 @@ struct FDSelect {
template <typename COUNT>
struct Subrs : CFFIndex<COUNT>
{
inline bool serialize (hb_serialize_context_t *c, const Subrs<COUNT> &subrs, unsigned int offSize, const hb_set_t *set, const ByteStr& nullStr = ByteStr())
{
TRACE_SERIALIZE (this);
if (&subrs == &Null(Subrs<COUNT>))
return_trace (true);
if ((subrs.count == 0) || (set == nullptr) || (hb_set_is_empty (set)))
{
if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
return_trace (false);
CFFIndex<COUNT>::count.set (0);
return_trace (true);
}
hb_vector_t<ByteStr> bytesArray;
bytesArray.init ();
if (!bytesArray.resize (subrs.count))
return_trace (false);
for (hb_codepoint_t i = 0; i < subrs.count; i++)
bytesArray[i] = (hb_set_has (set, i))? subrs[i]: nullStr;
bool result = CFFIndex<COUNT>::serialize (c, offSize, bytesArray);
bytesArray.fini ();
return_trace (result);
}
/* in parallel to above */
inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_set_t *set, unsigned int nullStrSize = 0) const
{
if (this == &Null(Subrs<COUNT>))
return 0;
unsigned int count_ = CFFIndex<COUNT>::count;
offSize = 0;
if ((count_ == 0) || (hb_set_get_population (set) == 0))
return COUNT::static_size;
unsigned int dataSize = 0;
for (hb_codepoint_t i = 0; i < count_; i++)
{
if (hb_set_has (set, i))
dataSize += (*this)[i].len;
else
dataSize += nullStrSize;
}
offSize = calcOffSize(dataSize);
return CFFIndex<COUNT>::calculate_serialized_size (offSize, count_, dataSize);
}
typedef COUNT count_type;
typedef CFFIndex<COUNT> SUPER;
};
} /* namespace CFF */

View File

@ -537,7 +537,7 @@ struct CFF1StringIndex : CFF1Index
return_trace (true);
}
hb_vector_t<ByteStr> bytesArray;
ByteStrArray bytesArray;
bytesArray.init ();
if (!bytesArray.resize (sidmap.get_count ()))
return_trace (false);
@ -757,7 +757,7 @@ struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal>
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr, val);
dictval.add_op (op, env.substr, val);
}
};
@ -806,7 +806,7 @@ struct CFF1FontDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
}
};
@ -828,11 +828,11 @@ struct CFF1PrivateDictValues_Base : DictValues<VAL>
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
size += DictValues<VAL>::getValue (i).str.len;
size += DictValues<VAL>::get_value (i).str.len;
return size;
}
@ -886,7 +886,7 @@ struct CFF1PrivateDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr, val);
dictval.add_op (op, env.substr, val);
}
};
@ -928,7 +928,7 @@ struct CFF1PrivateDictOpSet_Subset : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
}
};
@ -989,6 +989,7 @@ struct cff1
if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF1TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr);
topDict.init ();
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
}
@ -1041,12 +1042,14 @@ struct cff1
CFF1FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr);
font = fontDicts.push ();
font->init ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
PRIVDICTVAL *priv = &privateDicts[i];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr);
priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
@ -1064,6 +1067,7 @@ struct cff1
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr);
priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);

View File

@ -153,9 +153,9 @@ struct CFF2TopDictValues : TopDictValues<>
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < getNumValues (); i++)
for (unsigned int i = 0; i < get_count (); i++)
{
OpCode op = getValue (i).op;
OpCode op = get_value (i).op;
switch (op)
{
case OpCode_vstore:
@ -163,7 +163,7 @@ struct CFF2TopDictValues : TopDictValues<>
size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
break;
default:
size += TopDictValues<>::calculate_serialized_op_size (getValue (i));
size += TopDictValues<>::calculate_serialized_op_size (get_value (i));
break;
}
}
@ -183,7 +183,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
{
DictVal val;
val.init ();
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
env.clear_args ();
}
break;
@ -205,7 +205,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
}
typedef TopDictOpSet<> SUPER;
@ -246,7 +246,7 @@ struct CFF2FontDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
}
private:
@ -272,11 +272,11 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
size += DictValues<VAL>::getValue (i).str.len;
size += DictValues<VAL>::get_value (i).str.len;
return size;
}
@ -359,7 +359,7 @@ struct CFF2PrivateDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr, val);
dictval.add_op (op, env.substr, val);
}
};
@ -401,7 +401,7 @@ struct CFF2PrivateDictOpSet_Subset : DictOpSet
if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr);
dictval.add_op (op, env.substr);
}
private:
@ -453,6 +453,7 @@ struct cff2
if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF2TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr);
topDict.init ();
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
}
@ -484,12 +485,14 @@ struct cff2
CFF2FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr);
font = fontDicts.push ();
font->init ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv> priv_interp;
priv_interp.env.init(privDictStr);
privateDicts[i].init ();
if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);

View File

@ -35,74 +35,101 @@
namespace CFF {
/* Used for writing a temporary charstring */
struct ByteStrBuff : hb_vector_t<char, 1>
struct StrEncoder
{
inline bool encode_byte (unsigned char b)
inline StrEncoder (StrBuff &buff_)
: buff (buff_), error (false)
{}
inline void reset (void)
{
return (push ((const char)b) != &Crap(char));
buff.resize (0);
}
inline bool encode_int (int v)
inline void encode_byte (unsigned char b)
{
if (unlikely (buff.push ((const char)b) == &Crap(char)))
set_error ();
}
inline void encode_int (int v)
{
if ((-1131 <= v) && (v <= 1131))
{
if ((-107 <= v) && (v <= 107))
return encode_byte (v + 139);
encode_byte (v + 139);
else if (v > 0)
{
v -= 108;
return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF);
encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
encode_byte (v & 0xFF);
}
else
{
v = -v - 108;
return encode_byte ((v >> 8) + OpCode_TwoByteNegInt0) && encode_byte (v & 0xFF);
encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
encode_byte (v & 0xFF);
}
}
if (unlikely (v < -32768))
v = -32768;
else if (unlikely (v > 32767))
v = 32767;
return encode_byte (OpCode_shortint) &&
encode_byte ((v >> 8) & 0xFF) &&
encode_byte (v & 0xFF);
else
{
if (unlikely (v < -32768))
v = -32768;
else if (unlikely (v > 32767))
v = 32767;
encode_byte (OpCode_shortint);
encode_byte ((v >> 8) & 0xFF);
encode_byte (v & 0xFF);
}
}
inline bool encode_num (const Number& n)
inline void encode_num (const Number& n)
{
if (n.in_int_range ())
{
return encode_int (n.to_int ());
encode_int (n.to_int ());
}
else
{
int32_t v = n.to_fixed ();
return encode_byte (OpCode_fixedcs) &&
encode_byte ((v >> 24) & 0xFF) &&
encode_byte ((v >> 16) & 0xFF) &&
encode_byte ((v >> 8) & 0xFF) &&
encode_byte (v & 0xFF);
encode_byte (OpCode_fixedcs);
encode_byte ((v >> 24) & 0xFF);
encode_byte ((v >> 16) & 0xFF);
encode_byte ((v >> 8) & 0xFF);
encode_byte (v & 0xFF);
}
}
inline bool encode_op (OpCode op)
inline void encode_op (OpCode op)
{
if (Is_OpCode_ESC (op))
return encode_byte (OpCode_escape) &&
encode_byte (Unmake_OpCode_ESC (op));
{
encode_byte (OpCode_escape);
encode_byte (Unmake_OpCode_ESC (op));
}
else
return encode_byte (op);
encode_byte (op);
}
};
struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1>
{
inline void fini (void)
inline void copy_str (const ByteStr &str)
{
for (unsigned int i = 0; i < len; i++)
hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini ();
hb_vector_t<ByteStrBuff, 1>::fini ();
unsigned int offset = buff.len;
buff.resize (offset + str.len);
if (unlikely (buff.len < offset + str.len))
{
set_error ();
return;
}
memcpy (&buff[offset], &str.str[0], str.len);
}
inline bool is_error (void) const { return error; }
protected:
inline void set_error (void) { error = true; }
StrBuff &buff;
bool error;
};
struct CFFSubTableOffsets {
@ -215,8 +242,8 @@ struct CFFFontDict_OpSerializer : OpSerializer
struct CFFPrivateDict_OpSerializer : OpSerializer
{
inline CFFPrivateDict_OpSerializer (bool drop_hints_=false)
: drop_hints (drop_hints_) {}
inline CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
: desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
@ -227,7 +254,12 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return true;
if (opstr.op == OpCode_Subrs)
return_trace (true);
{
if (desubroutinize)
return_trace (true);
else
return_trace (FontDict::serialize_offset4_op (c, opstr.op, subrsOffset));
}
else
return_trace (copy_opstr (c, opstr));
}
@ -237,18 +269,24 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return 0;
if (opstr.op == OpCode_Subrs)
return 0;
{
if (desubroutinize)
return 0;
else
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
}
else
return opstr.str.len;
}
protected:
const bool desubroutinize;
const bool drop_hints;
};
struct FlattenParam
{
ByteStrBuff &flatStr;
StrBuff &flatStr;
bool drop_hints;
};
@ -263,7 +301,7 @@ struct SubrFlattener
drop_hints (drop_hints_)
{}
inline bool flatten (ByteStrBuffArray &flat_charstrings)
inline bool flatten (StrBuffArray &flat_charstrings)
{
if (!flat_charstrings.resize (glyphs.len))
return false;
@ -287,6 +325,594 @@ struct SubrFlattener
const hb_vector_t<hb_codepoint_t> &glyphs;
bool drop_hints;
};
struct SubrClosures
{
inline SubrClosures (void)
: valid (false),
global_closure (nullptr)
{
local_closures.init ();
}
inline void init (unsigned int fd_count)
{
valid = true;
global_closure = hb_set_create ();
if (global_closure == hb_set_get_empty ())
valid = false;
if (!local_closures.resize (fd_count))
valid = false;
for (unsigned int i = 0; i < local_closures.len; i++)
{
local_closures[i] = hb_set_create ();
if (local_closures[i] == hb_set_get_empty ())
valid = false;
}
}
inline void fini (void)
{
hb_set_destroy (global_closure);
for (unsigned int i = 0; i < local_closures.len; i++)
hb_set_destroy (local_closures[i]);
local_closures.fini ();
}
inline void reset (void)
{
hb_set_clear (global_closure);
for (unsigned int i = 0; i < local_closures.len; i++)
hb_set_clear (local_closures[i]);
}
bool is_valid (void) const { return valid; }
bool valid;
hb_set_t *global_closure;
hb_vector_t<hb_set_t *> local_closures;
};
struct ParsedCSOp : OpStr
{
inline void init (unsigned int subr_num_ = 0)
{
OpStr::init ();
flags = kDropFlag_None;
subr_num = subr_num_;
}
inline void fini (void)
{
OpStr::fini ();
}
inline bool for_keep (void) const { return (flags & kDropFlag_Keep) != 0; }
inline bool for_drop (void) const { return (flags & kDropFlag_Drop) != 0; }
inline void set_drop (void) { if (!for_keep ()) flags |= kDropFlag_Drop; }
inline void set_keep (void) { flags |= kDropFlag_Keep; }
enum DropFlag
{
kDropFlag_None = 0,
kDropFlag_Drop = 1,
kDropFlag_Keep = 2
};
unsigned int flags;
unsigned int subr_num;
};
struct ParsedCStr : ParsedValues<ParsedCSOp>
{
inline void init (void)
{
SUPER::init ();
parsed = false;
hint_removed = false;
has_prefix_ = false;
}
inline void add_op (OpCode op, const SubByteStr& substr)
{
if (!is_parsed ())
SUPER::add_op (op, substr);
}
inline void addCallOp (OpCode op, const SubByteStr& substr, unsigned int subr_num)
{
if (!is_parsed ())
{
unsigned int parsed_len = get_count ();
if (likely (parsed_len > 0))
values[parsed_len-1].set_drop ();
ParsedCSOp val;
val.init (subr_num);
SUPER::add_op (op, substr, val);
}
}
inline void set_prefix (const Number &num, OpCode op = OpCode_Invalid)
{
has_prefix_ = true;
prefix_op_ = op;
prefix_num_ = num;
}
inline bool is_parsed (void) const { return parsed; }
inline void set_parsed (void) { parsed = true; }
inline bool is_hint_removed (void) const { return hint_removed; }
inline void set_hint_removed (void) { hint_removed = true; }
inline bool has_prefix (void) const { return has_prefix_; }
inline OpCode prefix_op (void) const { return prefix_op_; }
inline const Number &prefix_num (void) const { return prefix_num_; }
protected:
bool parsed;
bool hint_removed;
bool has_prefix_;
OpCode prefix_op_;
Number prefix_num_;
private:
typedef ParsedValues<ParsedCSOp> SUPER;
};
struct ParsedCStrs : hb_vector_t<ParsedCStr>
{
inline void init (unsigned int len_ = 0)
{
hb_vector_t<ParsedCStr>::init ();
resize (len_);
for (unsigned int i = 0; i < len; i++)
(*this)[i].init ();
}
};
struct SubrSubsetParam
{
inline void init (ParsedCStr *parsed_charstring_,
ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_,
hb_set_t *global_closure_, hb_set_t *local_closure_,
bool drop_hints_)
{
parsed_charstring = parsed_charstring_;
current_parsed_str = parsed_charstring;
parsed_global_subrs = parsed_global_subrs_;
parsed_local_subrs = parsed_local_subrs_;
global_closure = global_closure_;
local_closure = local_closure_;
drop_hints = drop_hints_;
}
template <typename ENV>
inline void set_current_str (ENV &env)
{
const CallContext &context = env.context;
switch (context.type)
{
case CSType_CharString:
current_parsed_str = parsed_charstring;
break;
case CSType_LocalSubr:
if (likely (context.subr_num < parsed_local_subrs->len))
current_parsed_str = &(*parsed_local_subrs)[context.subr_num];
else
env.set_error ();
break;
case CSType_GlobalSubr:
if (likely (context.subr_num < parsed_global_subrs->len))
current_parsed_str = &(*parsed_global_subrs)[context.subr_num];
else
env.set_error ();
break;
default:
assert (0);
}
}
ParsedCStr *current_parsed_str;
ParsedCStr *parsed_charstring;
ParsedCStrs *parsed_global_subrs;
ParsedCStrs *parsed_local_subrs;
hb_set_t *global_closure;
hb_set_t *local_closure;
bool drop_hints;
};
struct SubrRemap : Remap
{
inline void create (hb_set_t *closure)
{
/* create a remapping of subroutine numbers from old to new.
* no optimization based on usage counts. fonttools doesn't appear doing that either.
*/
reset (closure->get_max () + 1);
for (hb_codepoint_t old_num = 0; old_num < len; old_num++)
{
if (hb_set_has (closure, old_num))
add (old_num);
}
if (get_count () < 1240)
bias = 107;
else if (get_count () < 33900)
bias = 1131;
else
bias = 32768;
}
inline hb_codepoint_t operator[] (unsigned int old_num) const
{
if (old_num >= len)
return CFF_UNDEF_CODE;
else
return Remap::operator[] (old_num);
}
inline int biased_num (unsigned int old_num) const
{
return (int)(*this)[old_num] - bias;
}
protected:
int bias;
};
struct SubrRemaps
{
inline SubrRemaps (void)
{
global_remap.init ();
local_remaps.init ();
}
inline ~SubrRemaps (void)
{
fini ();
}
inline void init (unsigned int fdCount)
{
local_remaps.resize (fdCount);
for (unsigned int i = 0; i < fdCount; i++)
local_remaps[i].init ();
}
inline void create (SubrClosures& closures)
{
global_remap.create (closures.global_closure);
for (unsigned int i = 0; i < local_remaps.len; i++)
local_remaps[i].create (closures.local_closures[i]);
}
inline void fini (void)
{
global_remap.fini ();
local_remaps.fini_deep ();
}
SubrRemap global_remap;
hb_vector_t<SubrRemap> local_remaps;
};
template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET>
struct SubrSubsetter
{
inline SubrSubsetter (void)
{
parsed_charstrings.init ();
parsed_global_subrs.init ();
parsed_local_subrs.init ();
}
inline ~SubrSubsetter (void)
{
closures.fini ();
remaps.fini ();
parsed_charstrings.fini_deep ();
parsed_global_subrs.fini_deep ();
parsed_local_subrs.fini_deep ();
}
/* Subroutine subsetting with --no-desubroutinize runs in phases:
*
* 1. execute charstrings/subroutines to determine subroutine closures
* 2. parse out all operators and numbers
* 3. mark hint operators and operands for removal if --no-hinting
* 4. re-encode all charstrings and subroutines with new subroutine numbers
*
* Phases #1 and #2 are done at the same time in collect_subrs ().
* Phase #3 requires walking charstrings/subroutines forward then backward (hence parsing), because
* we can't tell if a number belongs to a hint op until we see the first moveto.
*
* Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
* within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
*/
inline bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints)
{
closures.init (acc.fdCount);
remaps.init (acc.fdCount);
parsed_charstrings.init (glyphs.len);
parsed_global_subrs.init (acc.globalSubrs->count);
parsed_local_subrs.resize (acc.fdCount);
for (unsigned int i = 0; i < acc.fdCount; i++)
{
parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
}
if (unlikely (!closures.valid))
return false;
/* phase 1 & 2 */
for (unsigned int i = 0; i < glyphs.len; i++)
{
hb_codepoint_t glyph = glyphs[i];
const ByteStr str = (*acc.charStrings)[glyph];
unsigned int fd = acc.fdSelect->get_fd (glyph);
CSInterpreter<ENV, OPSET, SubrSubsetParam> interp;
interp.env.init (str, acc, fd);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
if (unlikely (!interp.interpret (param)))
return false;
/* copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
SUBSETTER::set_parsed_prefix (interp.env, parsed_charstrings[i]);
}
if (drop_hints)
{
/* mark hint ops and arguments for drop */
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
bool seen_moveto = false;
if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto))
parsed_charstrings[i].set_hint_removed ();
}
/* after dropping hints recreate closures from subrs actually used */
closures.reset ();
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
SubrSubsetParam param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
closures.global_closure, closures.local_closures[fd],
drop_hints);
collect_subr_refs_in_str (parsed_charstrings[i], param);
}
}
remaps.create (closures);
return true;
}
inline bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const
{
if (unlikely (!buffArray.resize (glyphs.len)))
return false;
for (unsigned int i = 0; i < glyphs.len; i++)
{
unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
return false;
}
return true;
}
inline bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, StrBuffArray &buffArray) const
{
unsigned int count = remap.get_count ();
if (unlikely (!buffArray.resize (count)))
return false;
for (unsigned int old_num = 0; old_num < subrs.len; old_num++)
{
hb_codepoint_t new_num = remap[old_num];
if (new_num != CFF_UNDEF_CODE)
{
if (unlikely (!encode_str (subrs[old_num], 0, buffArray[new_num])))
return false;
}
}
return true;
}
inline bool encode_globalsubrs (StrBuffArray &buffArray)
{
return encode_subrs (parsed_global_subrs, remaps.global_remap, buffArray);
}
inline bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const
{
return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], buffArray);
}
protected:
inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
ParsedCStrs &subrs, unsigned int subr_num,
const SubrSubsetParam &param, bool &seen_moveto)
{
if (drop_hints_in_str (subrs[subr_num], param, seen_moveto))
{
/* if the first op in the subr is a hint op, then all args/ops (especially including other subr calls)
* preceding this subr no and call op are hints */
/* TODO CFF2 vsindex */
for (unsigned int i = 0; i + 1 < pos; i++)
str.values[i].set_drop ();
return true;
}
else
return false;
}
/* returns true if it sees a hint op before moveto */
inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, bool &seen_moveto)
{
bool seen_hint = false;
unsigned int next_check_pos = 0;
for (unsigned int pos = 0; pos < str.values.len; pos++)
{
switch (str.values[pos].op)
{
case OpCode_callsubr:
seen_hint |= drop_hints_in_subr (str, pos,
*param.parsed_local_subrs, str.values[pos].subr_num,
param, seen_moveto);
break;
case OpCode_callgsubr:
seen_hint |= drop_hints_in_subr (str, pos,
*param.parsed_global_subrs, str.values[pos].subr_num,
param, seen_moveto);
break;
case OpCode_rmoveto:
case OpCode_hmoveto:
case OpCode_vmoveto:
seen_moveto = true;
break;
case OpCode_hintmask:
case OpCode_cntrmask:
if (seen_moveto)
{
str.values[pos].set_drop ();
break;
}
HB_FALLTHROUGH;
case OpCode_hstemhm:
case OpCode_vstemhm:
case OpCode_hstem:
case OpCode_vstem:
seen_hint = true;
for (unsigned int i = next_check_pos; i <= pos; i++)
{
/* TODO: CFF2 vsindex */
str.values[i].set_drop ();
}
next_check_pos = pos + 1;
break;
default:
/* NONE */
break;
}
}
return seen_hint;
}
inline void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos,
unsigned int subr_num, ParsedCStrs &subrs,
hb_set_t *closure,
const SubrSubsetParam &param)
{
hb_set_add (closure, subr_num);
collect_subr_refs_in_str (subrs[subr_num], param);
}
inline void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam &param)
{
for (unsigned int pos = 0; pos < str.values.len; pos++)
{
if (!str.values[pos].for_drop ())
{
switch (str.values[pos].op)
{
case OpCode_callsubr:
collect_subr_refs_in_subr (str, pos,
str.values[pos].subr_num, *param.parsed_local_subrs,
param.local_closure, param);
break;
case OpCode_callgsubr:
collect_subr_refs_in_subr (str, pos,
str.values[pos].subr_num, *param.parsed_global_subrs,
param.global_closure, param);
break;
default: break;
}
}
}
}
inline bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const
{
buff.init ();
StrEncoder encoder (buff);
encoder.reset ();
/* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
* re-insert it at the beginning of charstreing */
if (str.has_prefix () && str.is_hint_removed ())
{
encoder.encode_num (str.prefix_num ());
if (str.prefix_op () != OpCode_Invalid)
encoder.encode_op (str.prefix_op ());
}
for (unsigned int i = 0; i < str.get_count(); i++)
{
const ParsedCSOp &opstr = str.values[i];
if (!opstr.for_drop ())
{
switch (opstr.op)
{
case OpCode_callsubr:
encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
encoder.encode_op (OpCode_callsubr);
break;
case OpCode_callgsubr:
encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
encoder.encode_op (OpCode_callgsubr);
break;
default:
encoder.copy_str (opstr.str);
break;
}
}
}
return !encoder.is_error ();
}
protected:
SubrClosures closures;
ParsedCStrs parsed_charstrings;
ParsedCStrs parsed_global_subrs;
hb_vector_t<ParsedCStrs> parsed_local_subrs;
SubrRemaps remaps;
private:
typedef typename SUBRS::count_type subr_count_type;
};
}; /* namespace CFF */
HB_INTERNAL bool

View File

@ -92,18 +92,18 @@ struct CFF1TopDictValuesMod : CFF1TopDictValues
SUPER::fini ();
}
inline unsigned getNumValues (void) const
inline unsigned get_count (void) const
{
return base->getNumValues () + SUPER::getNumValues ();
return base->get_count () + SUPER::get_count ();
}
inline const CFF1TopDictVal &getValue (unsigned int i) const
inline const CFF1TopDictVal &get_value (unsigned int i) const
{
if (i < base->getNumValues ())
if (i < base->get_count ())
return (*base)[i];
else
return SUPER::values[i - base->getNumValues ()];
return SUPER::values[i - base->get_count ()];
}
inline const CFF1TopDictVal &operator [] (unsigned int i) const { return getValue (i); }
inline const CFF1TopDictVal &operator [] (unsigned int i) const { return get_value (i); }
inline void reassignSIDs (const RemapSID& sidmap)
{
@ -230,9 +230,9 @@ struct FontDictValuesMod
privateDictInfo = privateDictInfo_;
}
inline unsigned getNumValues (void) const
inline unsigned get_count (void) const
{
return base->getNumValues ();
return base->get_count ();
}
inline const OpStr &operator [] (unsigned int i) const { return (*base)[i]; }
@ -270,10 +270,9 @@ struct CFF1FontDict_OpSerializer : CFFFontDict_OpSerializer
struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
{
static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
{
start_arg = env.check_width ();
if ((start_arg > 0) && likely (param.flatStr.len == 0))
if (env.arg_start > 0)
flush_width (env, param);
switch (op)
@ -292,35 +291,40 @@ struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
HB_FALLTHROUGH;
default:
SUPER::flush_args_and_op (op, env, param, start_arg);
SUPER::flush_args_and_op (op, env, param);
break;
}
}
static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param)
{
for (unsigned int i = start_arg; i < env.argStack.get_count (); i++)
param.flatStr.encode_num (env.eval_arg (i));
SUPER::flush_args (env, param, start_arg);
StrEncoder encoder (param.flatStr);
for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
encoder.encode_num (env.eval_arg (i));
SUPER::flush_args (env, param);
}
static inline void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
{
param.flatStr.encode_op (op);
StrEncoder encoder (param.flatStr);
encoder.encode_op (op);
}
static inline void flush_width (CFF1CSInterpEnv &env, FlattenParam& param)
{
assert (env.has_width);
param.flatStr.encode_num (env.width);
StrEncoder encoder (param.flatStr);
encoder.encode_num (env.width);
}
static inline void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
{
SUPER::flush_hintmask (op, env, param);
if (!param.drop_hints)
{
StrEncoder encoder (param.flatStr);
for (unsigned int i = 0; i < env.hintmask_size; i++)
param.flatStr.encode_byte (env.substr[i]);
encoder.encode_byte (env.substr[i]);
}
}
private:
@ -342,6 +346,70 @@ struct RangeList : hb_vector_t<code_pair>
}
};
struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam>
{
static inline void process_op (OpCode op, CFF1CSInterpEnv &env, SubrSubsetParam& param)
{
switch (op) {
case OpCode_return:
param.current_parsed_str->add_op (op, env.substr);
param.current_parsed_str->set_parsed ();
env.returnFromSubr ();
param.set_current_str (env);
break;
case OpCode_endchar:
param.current_parsed_str->add_op (op, env.substr);
param.current_parsed_str->set_parsed ();
SUPER::process_op (op, env, param);
break;
case OpCode_callsubr:
process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
break;
case OpCode_callgsubr:
process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
break;
default:
SUPER::process_op (op, env, param);
param.current_parsed_str->add_op (op, env.substr);
break;
}
}
static inline void process_width (CFF1CSInterpEnv &env, SubrSubsetParam& param)
{
}
protected:
static inline void process_call_subr (OpCode op, CSType type,
CFF1CSInterpEnv &env, SubrSubsetParam& param,
CFF1BiasedSubrs& subrs, hb_set_t *closure)
{
SubByteStr substr = env.substr;
env.callSubr (subrs, type);
param.current_parsed_str->addCallOp (op, substr, env.context.subr_num);
hb_set_add (closure, env.context.subr_num);
param.set_current_str (env);
}
private:
typedef CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
};
struct CFF1SubrSubsetter : SubrSubsetter<CFF1SubrSubsetter, CFF1Subrs, const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubrSubset>
{
static inline void set_parsed_prefix (const CFF1CSInterpEnv &env, ParsedCStr &charstring)
{
if (env.processed_width)
charstring.set_prefix (env.width);
}
};
struct cff_subset_plan {
inline cff_subset_plan (void)
: final_size (0),
@ -349,7 +417,8 @@ struct cff_subset_plan {
orig_fdcount (0),
subset_fdcount (1),
subset_fdselect_format (0),
drop_hints (false)
drop_hints (false),
desubroutinize(false)
{
topdict_sizes.init ();
topdict_sizes.resize (1);
@ -357,7 +426,8 @@ struct cff_subset_plan {
subset_fdselect_ranges.init ();
fdmap.init ();
subset_charstrings.init ();
flat_charstrings.init ();
subset_globalsubrs.init ();
subset_localsubrs.init ();
fontdicts_mod.init ();
subset_enc_code_ranges.init ();
subset_enc_supp_codes.init ();
@ -374,7 +444,8 @@ struct cff_subset_plan {
subset_fdselect_ranges.fini ();
fdmap.fini ();
subset_charstrings.fini ();
flat_charstrings.fini ();
subset_globalsubrs.fini ();
subset_localsubrs.fini_deep ();
fontdicts_mod.fini ();
subset_enc_code_ranges.fini ();
subset_enc_supp_codes.init ();
@ -504,9 +575,9 @@ struct cff_subset_plan {
}
if (acc.fdArray != &Null(CFF1FDArray))
for (unsigned int fd = 0; fd < orig_fdcount; fd++)
if (!fdmap.excludes (fd))
(void)sidmap.add (acc.fontDicts[fd].fontName);
for (unsigned int i = 0; i < orig_fdcount; i++)
if (fdmap.includes (i))
(void)sidmap.add (acc.fontDicts[i].fontName);
return true;
}
@ -521,6 +592,7 @@ struct cff_subset_plan {
num_glyphs = plan->glyphs.len;
orig_fdcount = acc.fdCount;
drop_hints = plan->drop_hints;
desubroutinize = true; // plan->desubroutinize;
/* check whether the subset renumbers any glyph IDs */
gid_renum = false;
@ -546,14 +618,14 @@ struct cff_subset_plan {
{
/* Add encoding/charset to a (copy of) top dict as necessary */
topdict_mod.init (&acc.topDict);
bool need_to_add_enc = (subset_encoding && !acc.topDict.hasOp (OpCode_Encoding));
bool need_to_add_set = (subset_charset && !acc.topDict.hasOp (OpCode_charset));
bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
if (need_to_add_enc || need_to_add_set)
{
if (need_to_add_enc)
topdict_mod.addOp (OpCode_Encoding);
topdict_mod.add_op (OpCode_Encoding);
if (need_to_add_set)
topdict_mod.addOp (OpCode_charset);
topdict_mod.add_op (OpCode_charset);
}
offsets.topDictInfo.offset = final_size;
CFF1TopDict_OpSerializer topSzr;
@ -595,16 +667,54 @@ struct cff_subset_plan {
final_size += offsets.stringIndexInfo.size;
}
if (desubroutinize)
{
/* Flatten global & local subrs */
SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten>
flattener(acc, plan->glyphs, plan->drop_hints);
if (!flattener.flatten (flat_charstrings))
if (!flattener.flatten (subset_charstrings))
return false;
/* no global/local subroutines */
offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */
}
else
{
/* Subset subrs: collect used subroutines, leaving all unused ones behind */
if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
return false;
/* encode charstrings, global subrs, local subrs with new subroutine numbers */
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
return false;
if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
return false;
/* global subrs */
unsigned int dataSize = subset_globalsubrs.total_size ();
offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
/* local subrs */
if (!offsets.localSubrsInfos.resize (orig_fdcount))
return false;
if (!subset_localsubrs.resize (orig_fdcount))
return false;
for (unsigned int fd = 0; fd < orig_fdcount; fd++)
{
subset_localsubrs[fd].init ();
if (fdmap.includes (fd))
{
if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
return false;
unsigned int dataSize = subset_localsubrs[fd].total_size ();
offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
}
}
}
/* global subrs */
offsets.globalSubrsInfo.offset = final_size;
@ -640,7 +750,7 @@ struct cff_subset_plan {
CFF1FontDict_OpSerializer fontSzr;
unsigned int dictsSize = 0;
for (unsigned int i = 0; i < acc.fontDicts.len; i++)
if (!fdmap.excludes (i))
if (fdmap.includes (i))
dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
@ -650,14 +760,7 @@ struct cff_subset_plan {
/* CharStrings */
{
offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++)
{
ByteStrBuff &flatstr = flat_charstrings[i];
ByteStr str (&flatstr[0], flatstr.len);
subset_charstrings.push (str);
dataSize += flatstr.len;
}
unsigned int dataSize = subset_charstrings.total_size ();
offsets.charStringsInfo.offSize = calcOffSize (dataSize);
final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
}
@ -666,23 +769,26 @@ struct cff_subset_plan {
offsets.privateDictInfo.offset = final_size;
for (unsigned int i = 0; i < orig_fdcount; i++)
{
if (!fdmap.excludes (i))
if (fdmap.includes (i))
{
CFFPrivateDict_OpSerializer privSzr (plan->drop_hints);
CFFPrivateDict_OpSerializer privSzr (desubroutinize, plan->drop_hints);
unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
TableInfo privInfo = { final_size, priv_size, 0 };
FontDictValuesMod fontdict_mod;
fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo );
fontdicts_mod.push (fontdict_mod);
final_size += privInfo.size;
if (!plan->desubroutinize)
final_size += offsets.localSubrsInfos[i].size;
}
}
if (!acc.is_CID ())
offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
return ((subset_charstrings.len == plan->glyphs.len) &&
(fontdicts_mod.len == subset_fdcount));
return ((subset_charstrings.len == plan->glyphs.len)
&& (fontdicts_mod.len == subset_fdcount));
}
inline unsigned int get_final_size (void) const { return final_size; }
@ -703,11 +809,11 @@ struct cff_subset_plan {
* set to CFF_UNDEF_CODE if excluded from subset */
Remap fdmap;
hb_vector_t<ByteStr> subset_charstrings;
ByteStrBuffArray flat_charstrings;
StrBuffArray subset_charstrings;
StrBuffArray subset_globalsubrs;
hb_vector_t<StrBuffArray> subset_localsubrs;
hb_vector_t<FontDictValuesMod> fontdicts_mod;
bool flatten_subrs;
bool drop_hints;
bool gid_renum;
@ -723,6 +829,9 @@ struct cff_subset_plan {
RemapSID sidmap;
unsigned int topDictModSIDs[NameDictValues::ValCount];
bool desubroutinize;
CFF1SubrSubsetter subr_subsetter;
};
static inline bool _write_cff1 (const cff_subset_plan &plan,
@ -790,9 +899,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
{
assert (plan.offsets.globalSubrsInfo.offset != 0);
assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
if (unlikely (dest == nullptr)) return false;
dest->count.set (0);
if (plan.desubroutinize)
{
CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
if (unlikely (dest == nullptr)) return false;
dest->count.set (0);
}
else
{
CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
return false;
}
}
}
/* Encoding */
@ -893,7 +1016,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size;
bool result;
CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
if (unlikely (!result))
@ -904,6 +1027,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
}
}
if (!plan.desubroutinize)
{
for (unsigned int i = 0; i < acc.privateDicts.len; i++)
{
if (!plan.fdmap.excludes (i))
{
CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
return false;
}
}
}
}
assert (c.head == c.end);
c.end_serialize ();

View File

@ -77,7 +77,7 @@ struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<>
struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
{
static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
{
switch (op)
{
@ -100,29 +100,30 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
HB_FALLTHROUGH;
default:
SUPER::flush_args_and_op (op, env, param, start_arg);
SUPER::flush_args_and_op (op, env, param);
break;
}
}
static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param)
{
for (unsigned int i = start_arg; i < env.argStack.get_count ();)
for (unsigned int i = 0; i < env.argStack.get_count ();)
{
const BlendArg &arg = env.argStack[i];
if (arg.blending ())
{
assert ((arg.numValues > 0) && (env.argStack.get_count () - start_arg >= arg.numValues));
assert ((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues));
flatten_blends (arg, i, env, param);
i += arg.numValues;
}
else
{
param.flatStr.encode_num (arg);
StrEncoder encoder (param.flatStr);
encoder.encode_num (arg);
i++;
}
}
SUPER::flush_args (env, param, start_arg);
SUPER::flush_args (env, param);
}
static inline void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param)
@ -133,20 +134,22 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
const BlendArg &arg1 = env.argStack[i + j];
assert (arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
(arg1.deltas.len == env.get_region_count ()));
param.flatStr.encode_num (arg1);
StrEncoder encoder (param.flatStr);
encoder.encode_num (arg1);
}
/* flatten deltas for each value */
StrEncoder encoder (param.flatStr);
for (unsigned int j = 0; j < arg.numValues; j++)
{
const BlendArg &arg1 = env.argStack[i + j];
for (unsigned int k = 0; k < arg1.deltas.len; k++)
param.flatStr.encode_num (arg1.deltas[k]);
encoder.encode_num (arg1.deltas[k]);
}
/* flatten the number of values followed by blend operator */
param.flatStr.encode_int (arg.numValues);
param.flatStr.encode_op (OpCode_blendcs);
encoder.encode_int (arg.numValues);
encoder.encode_op (OpCode_blendcs);
}
static inline void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
{
switch (op)
@ -155,7 +158,8 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
case OpCode_endchar:
return;
default:
param.flatStr.encode_op (op);
StrEncoder encoder (param.flatStr);
encoder.encode_op (op);
}
}
@ -170,7 +174,8 @@ struct cff2_subset_plan {
orig_fdcount (0),
subset_fdcount(1),
subset_fdselect_format (0),
drop_hints (false)
drop_hints (false),
desubroutinize (false)
{
subset_fdselect_ranges.init ();
fdmap.init ();
@ -195,6 +200,7 @@ struct cff2_subset_plan {
orig_fdcount = acc.fdArray->count;
drop_hints = plan->drop_hints;
desubroutinize = plan->desubroutinize;
/* CFF2 header */
final_size += OT::cff2::static_size;
@ -260,7 +266,7 @@ struct cff2_subset_plan {
unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++)
{
ByteStrBuff &flatstr = flat_charstrings[i];
StrBuff &flatstr = flat_charstrings[i];
ByteStr str (&flatstr[0], flatstr.len);
subset_charstrings.push (str);
dataSize += flatstr.len;
@ -276,7 +282,7 @@ struct cff2_subset_plan {
if (!fdmap.excludes (i))
{
unsigned int priv_size;
CFFPrivateDict_OpSerializer privSzr (drop_hints);
CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints);
priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
TableInfo privInfo = { final_size, priv_size, 0 };
privateDictInfos.push (privInfo);
@ -300,11 +306,12 @@ struct cff2_subset_plan {
Remap fdmap;
hb_vector_t<ByteStr> subset_charstrings;
ByteStrBuffArray flat_charstrings;
ByteStrArray subset_charstrings;
StrBuffArray flat_charstrings;
hb_vector_t<TableInfo> privateDictInfos;
bool drop_hints;
bool desubroutinize;
};
static inline bool _write_cff2 (const cff2_subset_plan &plan,
@ -421,7 +428,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
bool result;
CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
if (unlikely (!result))
{

View File

@ -44,6 +44,7 @@ struct hb_subset_input_t
bool drop_hints : 1;
bool drop_layout : 1;
bool desubroutinize : 1;
/* TODO
*
* features

View File

@ -150,6 +150,7 @@ hb_subset_plan_create (hb_face_t *face,
plan->drop_hints = input->drop_hints;
plan->drop_layout = input->drop_layout;
plan->desubroutinize = input->desubroutinize;
plan->unicodes = hb_set_create();
plan->glyphs.init();
plan->source = hb_face_reference (face);

View File

@ -41,6 +41,7 @@ struct hb_subset_plan_t
bool drop_hints : 1;
bool drop_layout : 1;
bool desubroutinize : 1;
// For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes;

View File

@ -970,6 +970,8 @@ subset_options_t::add_options (option_parser_t *parser)
GOptionEntry entries[] =
{
{"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr},
{"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutinizes", nullptr},
{nullptr}
};
parser->add_group (entries,

View File

@ -669,6 +669,7 @@ struct subset_options_t : option_group_t
subset_options_t (option_parser_t *parser)
{
drop_hints = false;
desubroutinize = false;
add_options (parser);
}
@ -676,6 +677,7 @@ struct subset_options_t : option_group_t
void add_options (option_parser_t *parser);
hb_bool_t drop_hints;
hb_bool_t desubroutinize;
};
/* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */