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; bool error;
}; };
typedef hb_vector_t<ByteStr> ByteStrArray;
/* stack */ /* stack */
template <typename ELEM, int LIMIT> template <typename ELEM, int LIMIT>
struct Stack 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> template <typename ARG=Number>
struct InterpEnv struct InterpEnv
{ {

View File

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

View File

@ -50,50 +50,7 @@ struct DictVal : OpStr
typedef DictVal NumDictVal; typedef DictVal NumDictVal;
template <typename VAL> template <typename VAL> struct DictValues : ParsedValues<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 OPSTR=OpStr> template <typename OPSTR=OpStr>
struct TopDictValues : DictValues<OPSTR> struct TopDictValues : DictValues<OPSTR>

View File

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

View File

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

View File

@ -61,6 +61,18 @@ struct code_pair
hb_codepoint_t glyph; 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 */ /* CFF INDEX */
template <typename COUNT> template <typename COUNT>
struct CFFIndex struct CFFIndex
@ -95,7 +107,7 @@ struct CFFIndex
inline bool serialize (hb_serialize_context_t *c, inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_, unsigned int offSize_,
const hb_vector_t<ByteStr> &byteArray) const ByteStrArray &byteArray)
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
/* serialize CFFIndex header */ /* serialize CFFIndex header */
@ -126,6 +138,22 @@ struct CFFIndex
return_trace (true); 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) inline void set_offset_at (unsigned int index, unsigned int offset)
{ {
HBUINT8 *p = offsets + offSize * index + offSize; HBUINT8 *p = offsets + offSize * index + offSize;
@ -280,7 +308,7 @@ struct Dict : UnsizedByteStr
PARAM& param) PARAM& param)
{ {
TRACE_SERIALIZE (this); 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))) if (unlikely (!opszr.serialize (c, dictval[i], param)))
return_trace (false); return_trace (false);
@ -294,7 +322,7 @@ struct Dict : UnsizedByteStr
OP_SERIALIZER& opszr) OP_SERIALIZER& opszr)
{ {
unsigned int size = 0; 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]); size += opszr.calculate_serialized_size (dictval[i]);
return size; return size;
} }
@ -383,6 +411,9 @@ struct Remap : hb_vector_t<hb_codepoint_t>
inline bool excludes (hb_codepoint_t id) const inline bool excludes (hb_codepoint_t id) const
{ return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); } { 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 inline hb_codepoint_t operator[] (hb_codepoint_t i) const
{ {
if (fullset ()) if (fullset ())
@ -649,52 +680,8 @@ struct FDSelect {
template <typename COUNT> template <typename COUNT>
struct Subrs : CFFIndex<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()) typedef COUNT count_type;
{ typedef CFFIndex<COUNT> SUPER;
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);
}
}; };
} /* namespace CFF */ } /* namespace CFF */

View File

@ -537,7 +537,7 @@ struct CFF1StringIndex : CFF1Index
return_trace (true); return_trace (true);
} }
hb_vector_t<ByteStr> bytesArray; ByteStrArray bytesArray;
bytesArray.init (); bytesArray.init ();
if (!bytesArray.resize (sidmap.get_count ())) if (!bytesArray.resize (sidmap.get_count ()))
return_trace (false); return_trace (false);
@ -757,7 +757,7 @@ struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal>
if (unlikely (env.in_error ())) return; 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; 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 inline unsigned int calculate_serialized_size (void) const
{ {
unsigned int size = 0; unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++) for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
if (DictValues<VAL>::getValue (i).op == OpCode_Subrs) if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else else
size += DictValues<VAL>::getValue (i).str.len; size += DictValues<VAL>::get_value (i).str.len;
return size; return size;
} }
@ -886,7 +886,7 @@ struct CFF1PrivateDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return; 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; 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; } if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF1TopDict_Interpreter top_interp; CFF1TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr); top_interp.env.init (topDictStr);
topDict.init ();
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
} }
@ -1041,12 +1042,14 @@ struct cff1
CFF1FontDict_Interpreter font_interp; CFF1FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr); font_interp.env.init (fontDictStr);
font = fontDicts.push (); font = fontDicts.push ();
font->init ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; } if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
PRIVDICTVAL *priv = &privateDicts[i]; PRIVDICTVAL *priv = &privateDicts[i];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size); const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp; DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr); priv_interp.env.init (privDictStr);
priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset); priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
@ -1064,6 +1067,7 @@ struct cff1
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp; DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr); priv_interp.env.init (privDictStr);
priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset); 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 inline unsigned int calculate_serialized_size (void) const
{ {
unsigned int size = 0; 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) switch (op)
{ {
case OpCode_vstore: case OpCode_vstore:
@ -163,7 +163,7 @@ struct CFF2TopDictValues : TopDictValues<>
size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
break; break;
default: default:
size += TopDictValues<>::calculate_serialized_op_size (getValue (i)); size += TopDictValues<>::calculate_serialized_op_size (get_value (i));
break; break;
} }
} }
@ -183,7 +183,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
{ {
DictVal val; DictVal val;
val.init (); val.init ();
dictval.addOp (op, env.substr); dictval.add_op (op, env.substr);
env.clear_args (); env.clear_args ();
} }
break; break;
@ -205,7 +205,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<>
if (unlikely (env.in_error ())) return; if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr); dictval.add_op (op, env.substr);
} }
typedef TopDictOpSet<> SUPER; typedef TopDictOpSet<> SUPER;
@ -246,7 +246,7 @@ struct CFF2FontDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return; if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr); dictval.add_op (op, env.substr);
} }
private: private:
@ -272,11 +272,11 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
inline unsigned int calculate_serialized_size (void) const inline unsigned int calculate_serialized_size (void) const
{ {
unsigned int size = 0; unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++) for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
if (DictValues<VAL>::getValue (i).op == OpCode_Subrs) if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else else
size += DictValues<VAL>::getValue (i).str.len; size += DictValues<VAL>::get_value (i).str.len;
return size; return size;
} }
@ -359,7 +359,7 @@ struct CFF2PrivateDictOpSet : DictOpSet
if (unlikely (env.in_error ())) return; 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; if (unlikely (env.in_error ())) return;
dictval.addOp (op, env.substr); dictval.add_op (op, env.substr);
} }
private: private:
@ -453,6 +453,7 @@ struct cff2
if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF2TopDict_Interpreter top_interp; CFF2TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr); top_interp.env.init (topDictStr);
topDict.init ();
if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
} }
@ -484,12 +485,14 @@ struct cff2
CFF2FontDict_Interpreter font_interp; CFF2FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr); font_interp.env.init (fontDictStr);
font = fontDicts.push (); font = fontDicts.push ();
font->init ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; } if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv> priv_interp; DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv> priv_interp;
priv_interp.env.init(privDictStr); priv_interp.env.init(privDictStr);
privateDicts[i].init ();
if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset); privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);

View File

@ -35,74 +35,101 @@
namespace CFF { namespace CFF {
/* Used for writing a temporary charstring */ /* 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 ((-1131 <= v) && (v <= 1131))
{ {
if ((-107 <= v) && (v <= 107)) if ((-107 <= v) && (v <= 107))
return encode_byte (v + 139); encode_byte (v + 139);
else if (v > 0) else if (v > 0)
{ {
v -= 108; v -= 108;
return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF); encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
encode_byte (v & 0xFF);
} }
else else
{ {
v = -v - 108; 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)) else
v = -32768; {
else if (unlikely (v > 32767)) if (unlikely (v < -32768))
v = 32767; v = -32768;
return encode_byte (OpCode_shortint) && else if (unlikely (v > 32767))
encode_byte ((v >> 8) & 0xFF) && v = 32767;
encode_byte (v & 0xFF); 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 ()) if (n.in_int_range ())
{ {
return encode_int (n.to_int ()); encode_int (n.to_int ());
} }
else else
{ {
int32_t v = n.to_fixed (); int32_t v = n.to_fixed ();
return encode_byte (OpCode_fixedcs) && encode_byte (OpCode_fixedcs);
encode_byte ((v >> 24) & 0xFF) && encode_byte ((v >> 24) & 0xFF);
encode_byte ((v >> 16) & 0xFF) && encode_byte ((v >> 16) & 0xFF);
encode_byte ((v >> 8) & 0xFF) && encode_byte ((v >> 8) & 0xFF);
encode_byte (v & 0xFF); encode_byte (v & 0xFF);
} }
} }
inline bool encode_op (OpCode op) inline void encode_op (OpCode op)
{ {
if (Is_OpCode_ESC (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 else
return encode_byte (op); encode_byte (op);
} }
};
struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1> inline void copy_str (const ByteStr &str)
{
inline void fini (void)
{ {
for (unsigned int i = 0; i < len; i++) unsigned int offset = buff.len;
hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini (); buff.resize (offset + str.len);
hb_vector_t<ByteStrBuff, 1>::fini (); 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 { struct CFFSubTableOffsets {
@ -215,8 +242,8 @@ struct CFFFontDict_OpSerializer : OpSerializer
struct CFFPrivateDict_OpSerializer : OpSerializer struct CFFPrivateDict_OpSerializer : OpSerializer
{ {
inline CFFPrivateDict_OpSerializer (bool drop_hints_=false) inline CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
: drop_hints (drop_hints_) {} : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
inline bool serialize (hb_serialize_context_t *c, inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr, const OpStr &opstr,
@ -227,7 +254,12 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
if (drop_hints && DictOpSet::is_hint_op (opstr.op)) if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return true; return true;
if (opstr.op == OpCode_Subrs) 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 else
return_trace (copy_opstr (c, opstr)); return_trace (copy_opstr (c, opstr));
} }
@ -237,18 +269,24 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
if (drop_hints && DictOpSet::is_hint_op (opstr.op)) if (drop_hints && DictOpSet::is_hint_op (opstr.op))
return 0; return 0;
if (opstr.op == OpCode_Subrs) if (opstr.op == OpCode_Subrs)
return 0; {
if (desubroutinize)
return 0;
else
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
}
else else
return opstr.str.len; return opstr.str.len;
} }
protected: protected:
const bool desubroutinize;
const bool drop_hints; const bool drop_hints;
}; };
struct FlattenParam struct FlattenParam
{ {
ByteStrBuff &flatStr; StrBuff &flatStr;
bool drop_hints; bool drop_hints;
}; };
@ -263,7 +301,7 @@ struct SubrFlattener
drop_hints (drop_hints_) drop_hints (drop_hints_)
{} {}
inline bool flatten (ByteStrBuffArray &flat_charstrings) inline bool flatten (StrBuffArray &flat_charstrings)
{ {
if (!flat_charstrings.resize (glyphs.len)) if (!flat_charstrings.resize (glyphs.len))
return false; return false;
@ -287,6 +325,594 @@ struct SubrFlattener
const hb_vector_t<hb_codepoint_t> &glyphs; const hb_vector_t<hb_codepoint_t> &glyphs;
bool drop_hints; 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 */ }; /* namespace CFF */
HB_INTERNAL bool HB_INTERNAL bool

View File

@ -92,18 +92,18 @@ struct CFF1TopDictValuesMod : CFF1TopDictValues
SUPER::fini (); 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]; return (*base)[i];
else 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) inline void reassignSIDs (const RemapSID& sidmap)
{ {
@ -230,9 +230,9 @@ struct FontDictValuesMod
privateDictInfo = privateDictInfo_; 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]; } 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> 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 (env.arg_start > 0)
if ((start_arg > 0) && likely (param.flatStr.len == 0))
flush_width (env, param); flush_width (env, param);
switch (op) switch (op)
@ -292,35 +291,40 @@ struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
HB_FALLTHROUGH; HB_FALLTHROUGH;
default: default:
SUPER::flush_args_and_op (op, env, param, start_arg); SUPER::flush_args_and_op (op, env, param);
break; break;
} }
} }
static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param)
static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0)
{ {
for (unsigned int i = start_arg; i < env.argStack.get_count (); i++) StrEncoder encoder (param.flatStr);
param.flatStr.encode_num (env.eval_arg (i)); for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
SUPER::flush_args (env, param, start_arg); encoder.encode_num (env.eval_arg (i));
SUPER::flush_args (env, param);
} }
static inline void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& 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) static inline void flush_width (CFF1CSInterpEnv &env, FlattenParam& param)
{ {
assert (env.has_width); 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) static inline void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
{ {
SUPER::flush_hintmask (op, env, param); SUPER::flush_hintmask (op, env, param);
if (!param.drop_hints) if (!param.drop_hints)
{
StrEncoder encoder (param.flatStr);
for (unsigned int i = 0; i < env.hintmask_size; i++) for (unsigned int i = 0; i < env.hintmask_size; i++)
param.flatStr.encode_byte (env.substr[i]); encoder.encode_byte (env.substr[i]);
}
} }
private: 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 { struct cff_subset_plan {
inline cff_subset_plan (void) inline cff_subset_plan (void)
: final_size (0), : final_size (0),
@ -349,7 +417,8 @@ struct cff_subset_plan {
orig_fdcount (0), orig_fdcount (0),
subset_fdcount (1), subset_fdcount (1),
subset_fdselect_format (0), subset_fdselect_format (0),
drop_hints (false) drop_hints (false),
desubroutinize(false)
{ {
topdict_sizes.init (); topdict_sizes.init ();
topdict_sizes.resize (1); topdict_sizes.resize (1);
@ -357,7 +426,8 @@ struct cff_subset_plan {
subset_fdselect_ranges.init (); subset_fdselect_ranges.init ();
fdmap.init (); fdmap.init ();
subset_charstrings.init (); subset_charstrings.init ();
flat_charstrings.init (); subset_globalsubrs.init ();
subset_localsubrs.init ();
fontdicts_mod.init (); fontdicts_mod.init ();
subset_enc_code_ranges.init (); subset_enc_code_ranges.init ();
subset_enc_supp_codes.init (); subset_enc_supp_codes.init ();
@ -374,7 +444,8 @@ struct cff_subset_plan {
subset_fdselect_ranges.fini (); subset_fdselect_ranges.fini ();
fdmap.fini (); fdmap.fini ();
subset_charstrings.fini (); subset_charstrings.fini ();
flat_charstrings.fini (); subset_globalsubrs.fini ();
subset_localsubrs.fini_deep ();
fontdicts_mod.fini (); fontdicts_mod.fini ();
subset_enc_code_ranges.fini (); subset_enc_code_ranges.fini ();
subset_enc_supp_codes.init (); subset_enc_supp_codes.init ();
@ -504,9 +575,9 @@ struct cff_subset_plan {
} }
if (acc.fdArray != &Null(CFF1FDArray)) if (acc.fdArray != &Null(CFF1FDArray))
for (unsigned int fd = 0; fd < orig_fdcount; fd++) for (unsigned int i = 0; i < orig_fdcount; i++)
if (!fdmap.excludes (fd)) if (fdmap.includes (i))
(void)sidmap.add (acc.fontDicts[fd].fontName); (void)sidmap.add (acc.fontDicts[i].fontName);
return true; return true;
} }
@ -521,6 +592,7 @@ struct cff_subset_plan {
num_glyphs = plan->glyphs.len; num_glyphs = plan->glyphs.len;
orig_fdcount = acc.fdCount; orig_fdcount = acc.fdCount;
drop_hints = plan->drop_hints; drop_hints = plan->drop_hints;
desubroutinize = true; // plan->desubroutinize;
/* check whether the subset renumbers any glyph IDs */ /* check whether the subset renumbers any glyph IDs */
gid_renum = false; gid_renum = false;
@ -546,14 +618,14 @@ struct cff_subset_plan {
{ {
/* Add encoding/charset to a (copy of) top dict as necessary */ /* Add encoding/charset to a (copy of) top dict as necessary */
topdict_mod.init (&acc.topDict); topdict_mod.init (&acc.topDict);
bool need_to_add_enc = (subset_encoding && !acc.topDict.hasOp (OpCode_Encoding)); bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
bool need_to_add_set = (subset_charset && !acc.topDict.hasOp (OpCode_charset)); 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 || need_to_add_set)
{ {
if (need_to_add_enc) if (need_to_add_enc)
topdict_mod.addOp (OpCode_Encoding); topdict_mod.add_op (OpCode_Encoding);
if (need_to_add_set) if (need_to_add_set)
topdict_mod.addOp (OpCode_charset); topdict_mod.add_op (OpCode_charset);
} }
offsets.topDictInfo.offset = final_size; offsets.topDictInfo.offset = final_size;
CFF1TopDict_OpSerializer topSzr; CFF1TopDict_OpSerializer topSzr;
@ -595,16 +667,54 @@ struct cff_subset_plan {
final_size += offsets.stringIndexInfo.size; final_size += offsets.stringIndexInfo.size;
} }
if (desubroutinize)
{ {
/* Flatten global & local subrs */ /* Flatten global & local subrs */
SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten> SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten>
flattener(acc, plan->glyphs, plan->drop_hints); flattener(acc, plan->glyphs, plan->drop_hints);
if (!flattener.flatten (flat_charstrings)) if (!flattener.flatten (subset_charstrings))
return false; return false;
/* no global/local subroutines */ /* no global/local subroutines */
offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */ 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 */ /* global subrs */
offsets.globalSubrsInfo.offset = final_size; offsets.globalSubrsInfo.offset = final_size;
@ -640,7 +750,7 @@ struct cff_subset_plan {
CFF1FontDict_OpSerializer fontSzr; CFF1FontDict_OpSerializer fontSzr;
unsigned int dictsSize = 0; unsigned int dictsSize = 0;
for (unsigned int i = 0; i < acc.fontDicts.len; i++) 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); dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
@ -650,14 +760,7 @@ struct cff_subset_plan {
/* CharStrings */ /* CharStrings */
{ {
offsets.charStringsInfo.offset = final_size; offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = 0; unsigned int dataSize = subset_charstrings.total_size ();
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;
}
offsets.charStringsInfo.offSize = calcOffSize (dataSize); offsets.charStringsInfo.offSize = calcOffSize (dataSize);
final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize); final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
} }
@ -666,23 +769,26 @@ struct cff_subset_plan {
offsets.privateDictInfo.offset = final_size; offsets.privateDictInfo.offset = final_size;
for (unsigned int i = 0; i < orig_fdcount; i++) 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); unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
TableInfo privInfo = { final_size, priv_size, 0 }; TableInfo privInfo = { final_size, priv_size, 0 };
FontDictValuesMod fontdict_mod; FontDictValuesMod fontdict_mod;
fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo ); fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo );
fontdicts_mod.push (fontdict_mod); fontdicts_mod.push (fontdict_mod);
final_size += privInfo.size; final_size += privInfo.size;
if (!plan->desubroutinize)
final_size += offsets.localSubrsInfos[i].size;
} }
} }
if (!acc.is_CID ()) if (!acc.is_CID ())
offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo; offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
return ((subset_charstrings.len == plan->glyphs.len) && return ((subset_charstrings.len == plan->glyphs.len)
(fontdicts_mod.len == subset_fdcount)); && (fontdicts_mod.len == subset_fdcount));
} }
inline unsigned int get_final_size (void) const { return final_size; } 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 */ * set to CFF_UNDEF_CODE if excluded from subset */
Remap fdmap; Remap fdmap;
hb_vector_t<ByteStr> subset_charstrings; StrBuffArray subset_charstrings;
ByteStrBuffArray flat_charstrings; StrBuffArray subset_globalsubrs;
hb_vector_t<StrBuffArray> subset_localsubrs;
hb_vector_t<FontDictValuesMod> fontdicts_mod; hb_vector_t<FontDictValuesMod> fontdicts_mod;
bool flatten_subrs;
bool drop_hints; bool drop_hints;
bool gid_renum; bool gid_renum;
@ -723,6 +829,9 @@ struct cff_subset_plan {
RemapSID sidmap; RemapSID sidmap;
unsigned int topDictModSIDs[NameDictValues::ValCount]; unsigned int topDictModSIDs[NameDictValues::ValCount];
bool desubroutinize;
CFF1SubrSubsetter subr_subsetter;
}; };
static inline bool _write_cff1 (const cff_subset_plan &plan, 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 != 0);
assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start); assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
if (unlikely (dest == nullptr)) return false; if (plan.desubroutinize)
dest->count.set (0); {
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 */ /* Encoding */
@ -893,7 +1016,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
if (unlikely (pd == nullptr)) return false; if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size; unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size;
bool result; 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 */ /* 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); result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
if (unlikely (!result)) 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); assert (c.head == c.end);
c.end_serialize (); c.end_serialize ();

View File

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

View File

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

View File

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

View File

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

View File

@ -970,6 +970,8 @@ subset_options_t::add_options (option_parser_t *parser)
GOptionEntry entries[] = GOptionEntry entries[] =
{ {
{"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr}, {"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} {nullptr}
}; };
parser->add_group (entries, parser->add_group (entries,

View File

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