bool error;
};
+typedef hb_vector_t<ByteStr> ByteStrArray;
+
/* stack */
template <typename ELEM, int LIMIT>
struct Stack
}
};
+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
{
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
{
InterpEnv<ARG>::init (str);
+ context.init (str, CSType_CharString);
seen_moveto = true;
seen_hintmask = false;
hstem_count = 0;
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)
inline void moveto (const Point &pt_ ) { pt = pt_; }
public:
+ CallContext context;
bool endchar_flag;
bool seen_moveto;
bool seen_hintmask;
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;
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);
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)
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)
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;
};
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>
using namespace OT;
+typedef BiasedSubrs<CFF1Subrs> CFF1BiasedSubrs;
+
struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
{
template <typename ACC>
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
processed_width = false;
has_width = false;
+ arg_start = 0;
}
inline void fini (void)
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:
{
/* 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)
+ {
+ 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)
{
- start_arg = env.check_width ();
- SUPER::flush_args (env, param, start_arg);
+ SUPER::flush_args (env, param);
env.clear_args (); /* pop off width */
}
};
typedef InterpEnv<BlendArg> BlendInterpEnv;
+typedef BiasedSubrs<CFF2Subrs> CFF2BiasedSubrs;
struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
{
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
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 */
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;
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);
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;
}
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 ())
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 */
return_trace (true);
}
- hb_vector_t<ByteStr> bytesArray;
+ ByteStrArray bytesArray;
bytesArray.init ();
if (!bytesArray.resize (sidmap.get_count ()))
return_trace (false);
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr, val);
+ dictval.add_op (op, env.substr, val);
}
};
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
}
};
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;
}
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr, val);
+ dictval.add_op (op, env.substr, val);
}
};
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
}
};
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; }
}
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);
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);
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:
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;
}
}
{
DictVal val;
val.init ();
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
env.clear_args ();
}
break;
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
}
typedef TopDictOpSet<> SUPER;
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
}
private:
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;
}
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr, val);
+ dictval.add_op (op, env.substr, val);
}
};
if (unlikely (env.in_error ())) return;
- dictval.addOp (op, env.substr);
+ dictval.add_op (op, env.substr);
}
private:
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; }
}
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);
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)
+ {
+ buff.resize (0);
+ }
+
+ inline void encode_byte (unsigned char b)
{
- return (push ((const char)b) != &Crap(char));
+ if (unlikely (buff.push ((const char)b) == &Crap(char)))
+ set_error ();
}
- inline bool encode_int (int v)
+ 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 {
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,
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));
}
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;
};
drop_hints (drop_hints_)
{}
- inline bool flatten (ByteStrBuffArray &flat_charstrings)
+ inline bool flatten (StrBuffArray &flat_charstrings)
{
if (!flat_charstrings.resize (glyphs.len))
return false;
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 ¶m, 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 ¶m, 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 ¶m)
+ {
+ 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 ¶m)
+ {
+ 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
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)
{
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]; }
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)
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:
}
};
+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),
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);
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 ();
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 ();
}
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;
}
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;
{
/* 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;
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;
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);
/* 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);
}
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; }
* 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;
RemapSID sidmap;
unsigned int topDictModSIDs[NameDictValues::ValCount];
+
+ bool desubroutinize;
+ CFF1SubrSubsetter subr_subsetter;
};
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 */
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))
}
}
+ 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 ();
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)
{
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)
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)
case OpCode_endchar:
return;
default:
- param.flatStr.encode_op (op);
+ StrEncoder encoder (param.flatStr);
+ encoder.encode_op (op);
}
}
orig_fdcount (0),
subset_fdcount(1),
subset_fdselect_format (0),
- drop_hints (false)
+ drop_hints (false),
+ desubroutinize (false)
{
subset_fdselect_ranges.init ();
fdmap.init ();
orig_fdcount = acc.fdArray->count;
drop_hints = plan->drop_hints;
+ desubroutinize = plan->desubroutinize;
/* CFF2 header */
final_size += OT::cff2::static_size;
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;
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);
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,
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))
{
bool drop_hints : 1;
bool drop_layout : 1;
+ bool desubroutinize : 1;
/* TODO
*
* features
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);
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;
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,
subset_options_t (option_parser_t *parser)
{
drop_hints = false;
+ desubroutinize = false;
add_options (parser);
}
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 */