hb-ot-var-fvar-table.hh \
hb-ot-var-hvar-table.hh \
hb-ot-var-mvar-table.hh \
- hb-cff-interp-common-private.hh \
- hb-cff-interp-cs-common-private.hh \
++ hb-ot-cff-common.hh \
++ hb-cff-interp-common.hh \
++ hb-cff-interp-cs-common.hh \
+ hb-cff1-interp-cs.hh \
+ hb-cff2-interp-cs.hh \
- hb-cff-interp-dict-common-private.hh \
++ hb-cff-interp-dict-common.hh \
$(NULL)
HB_OT_RAGEL_GENERATED_sources = \
HB_SUBSET_sources = \
hb-static.cc \
hb-subset.cc \
+ hb-subset.hh \
hb-subset-glyf.cc \
- hb-subset-cff1.cc \
- hb-subset-cff2.cc \
- hb-subset-cff-common-private.cc \
+ hb-subset-glyf.hh \
++ hb-subset-cff1.cc \
++ hb-subset-cff2.cc \
++ hb-subset-cff-common.cc \
hb-subset-input.cc \
hb-subset-plan.cc \
+ hb-subset-plan.hh \
$(NULL)
HB_SUBSET_headers = \
hb-subset.h \
- hb-subset-cff1.hh \
- hb-subset-cff2.hh \
- hb-subset-cff-common-private.hh \
+ hb-subset-glyf.hh \
- hb-subset-private.hh \
++ hb-subset-cff1.hh \
++ hb-subset-cff2.hh \
++ hb-subset-cff-common.hh \
+ hb-subset-plan.hh \
++ hb-subset.hh \
$(NULL)
HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
--- /dev/null
- #include "hb-private.hh"
- #include "hb-cff-interp-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF_INTERP_CS_COMMON_PRIVATE_HH
+#define HB_CFF_INTERP_CS_COMMON_PRIVATE_HH
+
++#include "hb.hh"
++#include "hb-cff-interp-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+/* call stack */
+struct CallStack : Stack<SubByteStr, 10> {};
+
+template <typename SUBRS>
+struct BiasedSubrs
+{
+ inline void init (const SUBRS &subrs_)
+ {
+ subrs = &subrs_;
+ unsigned int nSubrs = subrs_.count;
+ if (nSubrs < 1240)
+ bias = 107;
+ else if (nSubrs < 33900)
+ bias = 1131;
+ else
+ bias = 32768;
+ }
+
+ inline void fini (void) {}
+
+ const SUBRS *subrs;
+ unsigned int bias;
+};
+
+template <typename SUBRS>
+struct CSInterpEnv : InterpEnv
+{
+ inline void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
+ {
+ InterpEnv::init (str);
+
+ stack_cleared = false;
+ seen_moveto = true;
+ seen_hintmask = false;
+ hstem_count = 0;
+ vstem_count = 0;
+ callStack.init ();
+ globalSubrs.init (globalSubrs_);
+ localSubrs.init (localSubrs_);
+ }
+ inline void fini (void)
+ {
+ InterpEnv::fini ();
+
+ callStack.fini ();
+ globalSubrs.fini ();
+ localSubrs.fini ();
+ }
+
+ inline bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
+ {
+ int n;
+ if (unlikely ((!callStack.check_overflow (1) ||
+ !argStack.check_pop_int (n))))
+ return false;
+ n += biasedSubrs.bias;
+ if (unlikely ((n < 0) || (n >= biasedSubrs.subrs->count)))
+ return false;
+
+ subr_num = (unsigned int)n;
+ return true;
+ }
+
+ inline bool callSubr (const BiasedSubrs<SUBRS>& biasedSubrs)
+ {
+ unsigned int subr_num;
+
+ if (unlikely (!popSubrNum (biasedSubrs, subr_num)))
+ return false;
+ callStack.push (substr);
+ substr = (*biasedSubrs.subrs)[subr_num];
+
+ return true;
+ }
+
+ inline bool returnFromSubr (void)
+ {
+ if (unlikely (!callStack.check_underflow ()))
+ return false;
+
+ substr = callStack.pop ();
+ return true;
+ }
+
+ inline void determine_hintmask_size (void)
+ {
+ if (!seen_hintmask)
+ {
+ vstem_count += argStack.size / 2;
+ hintmask_size = (hstem_count + vstem_count + 7) >> 3;
+ seen_hintmask = true;
+ }
+ }
+
+ inline void clear_stack (void)
+ {
+ stack_cleared = true;
+ argStack.clear ();
+ }
+
+ inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
+ inline bool is_endchar (void) const { return endchar_flag; }
+ inline bool is_stack_cleared (void) const { return stack_cleared; }
+
+ public:
+ bool endchar_flag;
+ bool stack_cleared;
+ bool seen_moveto;
+ bool seen_hintmask;
+
+ unsigned int hstem_count;
+ unsigned int vstem_count;
+ unsigned int hintmask_size;
+ CallStack callStack;
+ BiasedSubrs<SUBRS> globalSubrs;
+ BiasedSubrs<SUBRS> localSubrs;
+};
+
+template <typename OPSET, typename ENV, typename PARAM>
+struct CSOpSet : OpSet
+{
+ static inline bool process_op (OpCode op, ENV &env, PARAM& param)
+ {
+ switch (op) {
+
+ case OpCode_return:
+ return env.returnFromSubr ();
+ case OpCode_endchar:
+ env.set_endchar (true);
+ return true;
+
+ case OpCode_fixedcs:
+ return env.argStack.push_fixed_from_substr (env.substr);
+
+ case OpCode_callsubr:
+ return env.callSubr (env.localSubrs);
+
+ case OpCode_callgsubr:
+ return env.callSubr (env.globalSubrs);
+
+ case OpCode_hstem:
+ case OpCode_hstemhm:
+ OPSET::process_hstem (env, param);
+ break;
+ case OpCode_vstem:
+ case OpCode_vstemhm:
+ OPSET::process_vstem (env, param);
+ break;
+ case OpCode_hintmask:
+ case OpCode_cntrmask:
+ env.determine_hintmask_size ();
+ OPSET::flush_stack (env, param);
+ if (unlikely (!env.substr.avail (env.hintmask_size)))
+ return false;
+ env.substr.inc (env.hintmask_size);
+ break;
+
+ case OpCode_vmoveto:
+ case OpCode_rlineto:
+ case OpCode_hlineto:
+ case OpCode_vlineto:
+ case OpCode_rmoveto:
+ case OpCode_hmoveto:
+ OPSET::process_moveto (env, param);
+ break;
+ case OpCode_rrcurveto:
+ case OpCode_rcurveline:
+ case OpCode_rlinecurve:
+ case OpCode_vvcurveto:
+ case OpCode_hhcurveto:
+ case OpCode_vhcurveto:
+ case OpCode_hvcurveto:
+ case OpCode_hflex:
+ case OpCode_flex:
+ case OpCode_hflex1:
+ case OpCode_flex1:
+ OPSET::flush_stack (env, param);
+ break;
+
+ default:
+ return OpSet::process_op (op, env);
+ }
+ return true;
+ }
+
+ static inline void process_hstem (ENV &env, PARAM& param)
+ {
+ env.hstem_count += env.argStack.size / 2;
+ OPSET::flush_stack (env, param);
+ }
+
+ static inline void process_vstem (ENV &env, PARAM& param)
+ {
+ env.vstem_count += env.argStack.size / 2;
+ OPSET::flush_stack (env, param);
+ }
+
+ static inline void process_moveto (ENV &env, PARAM& param)
+ {
+ if (!env.seen_moveto)
+ {
+ env.determine_hintmask_size ();
+ env.seen_moveto = true;
+ }
+ OPSET::flush_stack (env, param);
+ }
+
+ static inline void flush_stack (ENV &env, PARAM& param)
+ {
+ env.clear_stack ();
+ }
+
+ /* numeric / logical / arithmetic operators */
+ static inline bool is_arg_op (OpCode op)
+ {
+ switch (op)
+ {
+ case OpCode_shortint:
+ case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+ case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+ case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+ case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+ case OpCode_fixedcs:
+ case OpCode_and:
+ case OpCode_or:
+ case OpCode_not:
+ case OpCode_abs:
+ case OpCode_add:
+ case OpCode_sub:
+ case OpCode_div:
+ case OpCode_neg:
+ case OpCode_eq:
+ case OpCode_drop:
+ case OpCode_put:
+ case OpCode_get:
+ case OpCode_ifelse:
+ case OpCode_random:
+ case OpCode_mul:
+ case OpCode_sqrt:
+ case OpCode_dup:
+ case OpCode_exch:
+ case OpCode_index:
+ case OpCode_roll:
+ return true;
+ default:
+ return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
+ }
+ }
+
+ static inline bool is_subr_op (OpCode op)
+ {
+ switch (op)
+ {
+ case OpCode_callsubr:
+ case OpCode_callgsubr:
+ case OpCode_return:
+ return true;
+ default:
+ return false;
+ }
+ }
+};
+
+template <typename ENV, typename OPSET, typename PARAM>
+struct CSInterpreter : Interpreter<ENV>
+{
+ inline bool interpret (PARAM& param)
+ {
+ param.init ();
+ Interpreter<ENV> &super = *this;
+ super.env.set_endchar (false);
+
+ for (;;) {
+ OpCode op;
+ if (unlikely (!super.env.fetch_op (op) ||
+ !OPSET::process_op (op, super.env, param)))
+ return false;
+ if (super.env.is_endchar ())
+ break;
+ }
+
+ return true;
+ }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_CS_COMMON_PRIVATE_HH */
--- /dev/null
- #include "hb-cff-interp-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF_INTERP_DICT_COMMON_PRIVATE_HH
+#define HB_CFF_INTERP_DICT_COMMON_PRIVATE_HH
+
++#include "hb-cff-interp-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+/* an opstr and the parsed out dict value(s) */
+struct DictVal : OpStr
+{
+ inline void init (void)
+ {
+ single_val.set_int (0);
+ multi_val.init ();
+ }
+
+ inline void fini (void)
+ {
+ multi_val.fini ();
+ }
+
+ Number single_val;
+ hb_vector_t<Number> multi_val;
+};
+
+template <typename VAL>
+struct DictValues
+{
+ inline void init (void)
+ {
+ opStart = 0;
+ values.init ();
+ }
+
+ inline void fini (void)
+ {
+ values.fini ();
+ }
+
+ inline void pushVal (OpCode op, const SubByteStr& substr)
+ {
+ VAL *val = values.push ();
+ val->op = op;
+ val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+ opStart = substr.offset;
+ }
+
+ inline void pushVal (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;
+ }
+
+ unsigned int opStart;
+ hb_vector_t<VAL> values;
+};
+
+struct TopDictValues : DictValues<OpStr>
+{
+ inline void init (void)
+ {
+ DictValues<OpStr>::init ();
+ charStringsOffset = 0;
+ FDArrayOffset = 0;
+ }
+
+ inline void fini (void)
+ {
+ DictValues<OpStr>::fini ();
+ }
+
+ inline unsigned int calculate_serialized_op_size (const OpStr& opstr) const
+ {
+ switch (opstr.op)
+ {
+ case OpCode_CharStrings:
+ case OpCode_FDArray:
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+ default:
+ return opstr.str.len;
+ }
+ }
+
+ unsigned int charStringsOffset;
+ unsigned int FDArrayOffset;
+};
+
+struct DictOpSet : OpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env)
+ {
+ switch (op) {
+ case OpCode_longintdict: /* 5-byte integer */
+ return env.argStack.push_longint_from_substr (env.substr);
+ case OpCode_BCD: /* real number */
+ float v;
+ if (unlikely (!env.argStack.check_overflow (1) || !parse_bcd (env.substr, v)))
+ return false;
+ env.argStack.push_real (v);
+ return true;
+
+ default:
+ return OpSet::process_op (op, env);
+ }
+
+ return true;
+ }
+
+ static inline bool is_hint_op (OpCode op)
+ {
+ switch (op)
+ {
+ case OpCode_BlueValues:
+ case OpCode_OtherBlues:
+ case OpCode_FamilyBlues:
+ case OpCode_FamilyOtherBlues:
+ case OpCode_StemSnapH:
+ case OpCode_StemSnapV:
+ case OpCode_StdHW:
+ case OpCode_StdVW:
+ case OpCode_BlueScale:
+ case OpCode_BlueShift:
+ case OpCode_BlueFuzz:
+ case OpCode_ForceBold:
+ case OpCode_LanguageGroup:
+ case OpCode_ExpansionFactor:
+ return true;
+ default:
+ return false;
+ }
+ }
+};
+
+struct TopDictOpSet : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, TopDictValues& dictval)
+ {
+ switch (op) {
+ case OpCode_CharStrings:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.charStringsOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_FDArray:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.FDArrayOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+ default:
+ return DictOpSet::process_op (op, env);
+ }
+
+ return true;
+ }
+};
+
+template <typename OPSET, typename PARAM>
+struct DictInterpreter : Interpreter<InterpEnv>
+{
+ inline bool interpret (PARAM& param)
+ {
+ param.init ();
+ Interpreter<InterpEnv> &super = *this;
+ do
+ {
+ OpCode op;
+ if (unlikely (!super.env.fetch_op (op) ||
+ !OPSET::process_op (op, super.env, param)))
+ return false;
+ } while (super.env.substr.avail ());
+
+ return true;
+ }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_DICT_COMMON_PRIVATE_HH */
--- /dev/null
- #include "hb-private.hh"
- #include "hb-cff-interp-cs-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF1_INTERP_CS_HH
+#define HB_CFF1_INTERP_CS_HH
+
++#include "hb.hh"
++#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
+{
+ inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs)
+ {
+ CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs);
+ processed_width = false;
+ has_width = false;
+ for (unsigned int i = 0; i < kTransientArraySize; i++)
+ transient_array[i].set_int (0);
+ }
+
+ bool check_transient_array_index (unsigned int i) const
+ { return i < kTransientArraySize; }
+
+ inline void check_width (void)
+ {
+ if (!processed_width)
+ {
+ if ((this->argStack.size & 1) != 0)
+ {
+ width = this->argStack.elements[0];
+ has_width = true;
+ }
+ processed_width = true;
+ }
+ }
+
+ bool processed_width;
+ bool has_width;
+ Number width;
+
+ static const unsigned int kTransientArraySize = 32;
+ Number transient_array[kTransientArraySize];
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF1CSOpSet : CSOpSet<OPSET, CFF1CSInterpEnv, PARAM>
+{
+ static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+ {
+ Number n1, n2;
+
+ switch (op) {
+
+ case OpCode_and:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_int ((n1.to_real() != 0.0f) && (n2.to_real() != 0.0f));
+ break;
+ case OpCode_or:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_int ((n1.to_real() != 0.0f) || (n2.to_real() != 0.0f));
+ break;
+ case OpCode_not:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ env.argStack.push_int (n1.to_real() == 0.0f);
+ break;
+ case OpCode_abs:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ env.argStack.push_real (fabs(n1.to_real ()));
+ break;
+ case OpCode_add:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_real (n1.to_real() + n2.to_real());
+ break;
+ case OpCode_sub:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_real (n1.to_real() - n2.to_real());
+ break;
+ case OpCode_div:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ if (unlikely (n2.to_real() == 0.0f))
+ env.argStack.push_int (0);
+ else
+ env.argStack.push_real (n1.to_real() / n2.to_real());
+ break;
+ case OpCode_neg:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ env.argStack.push_real (-n1.to_real ());
+ break;
+ case OpCode_eq:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_int (n1.to_real() == n2.to_real());
+ break;
+ case OpCode_drop:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ break;
+ case OpCode_put:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2) ||
+ !env.check_transient_array_index (n2.to_int ()))) return false;
+ env.transient_array[n2.to_int ()] = n1;
+ break;
+ case OpCode_get:
+ if (unlikely (!env.argStack.check_pop_num (n1) ||
+ !env.check_transient_array_index (n1.to_int ()))) return false;
+ env.argStack.push (env.transient_array[n1.to_int ()]);
+ break;
+ case OpCode_ifelse:
+ {
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ bool test = n1.to_real () <= n2.to_real ();
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push (test? n1: n2);
+ }
+ break;
+ case OpCode_random:
+ if (unlikely (!env.argStack.check_overflow (1))) return false;
+ env.argStack.push_int (1); /* we can't deal with random behavior; make it constant */
+ case OpCode_mul:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push_real (n1.to_real() * n2.to_real());
+ break;
+ case OpCode_sqrt:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ env.argStack.push_real ((float)sqrt (n1.to_real ()));
+ break;
+ case OpCode_dup:
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ env.argStack.push (n1);
+ env.argStack.push (n1);
+ break;
+ case OpCode_exch:
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ env.argStack.push (n2);
+ env.argStack.push (n1);
+ break;
+ case OpCode_index:
+ {
+ if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+ int i = n1.to_int ();
+ if (i < 0) i = 0;
+ if (unlikely (i >= env.argStack.size || !env.argStack.check_overflow (1))) return false;
+ env.argStack.push (env.argStack.elements[env.argStack.size - i - 1]);
+ }
+ break;
+ case OpCode_roll:
+ {
+ if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+ int n = n1.to_int ();
+ int j = n2.to_int ();
+ if (unlikely (n < 0 || n > env.argStack.size)) return false;
+ if (likely (n > 0))
+ {
+ if (j < 0)
+ j = n - (-j % n);
+ j %= n;
+ unsigned int top = env.argStack.size - 1;
+ unsigned int bot = top - n + 1;
+ env.argStack.reverse_range (top - j + 1, top);
+ env.argStack.reverse_range (bot, top - j);
+ env.argStack.reverse_range (bot, top);
+ }
+ }
+ break;
+ default:
+ if (unlikely (!SUPER::process_op (op, env, param)))
+ return false;
+ break;
+ }
+ return true;
+ }
+
+ static inline void flush_stack (CFF1CSInterpEnv &env, PARAM& param)
+ {
+ env.check_width ();
+ SUPER::flush_stack (env, param);
+ }
+
+ private:
+ typedef CSOpSet<OPSET, CFF1CSInterpEnv, PARAM> SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF1CSInterpreter : CSInterpreter<CFF1CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF1_INTERP_CS_HH */
--- /dev/null
- #include "hb-private.hh"
- #include "hb-cff-interp-cs-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF2_INTERP_CS_HH
+#define HB_CFF2_INTERP_CS_HH
+
++#include "hb.hh"
++#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
+{
+ inline void init (const ByteStr &str, const CFF2Subrs &globalSubrs_, const CFF2Subrs &localSubrs_)
+ {
+ CSInterpEnv<CFF2Subrs>::init (str, globalSubrs_, localSubrs_);
+ ivs = 0;
+ }
+
+ inline bool fetch_op (OpCode &op)
+ {
+ if (unlikely (this->substr.avail ()))
+ return CSInterpEnv<CFF2Subrs>::fetch_op (op);
+
+ /* make up return or endchar op */
+ if (this->callStack.check_underflow ())
+ op = OpCode_return;
+ else
+ op = OpCode_endchar;
+ return true;
+ }
+
+ inline unsigned int get_ivs (void) const { return ivs; }
+ inline void set_ivs (unsigned int ivs_) { ivs = ivs_; }
+
+ protected:
+ unsigned int ivs;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSOpSet : CSOpSet<OPSET, CFF2CSInterpEnv, PARAM>
+{
+ static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
+ {
+ switch (op) {
+
+ case OpCode_blendcs:
+ //env.flush_stack (); // XXX: TODO
+ break;
+ case OpCode_vsindexcs:
+ {
+ unsigned int ivs;
+ if (unlikely (!env.argStack.check_pop_uint (ivs))) return false;
+ env.set_ivs (ivs);
+ //env.flush_stack ();
+ }
+ break;
+ default:
+ typedef CSOpSet<OPSET, CFF2CSInterpEnv, PARAM> SUPER;
+ if (unlikely (!SUPER::process_op (op, env, param)))
+ return false;
+ break;
+ }
+ return true;
+ }
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF2_INTERP_CS_HH */
--- /dev/null
- #include "hb-open-type-private.hh"
- #include "hb-ot-layout-common-private.hh"
- #include "hb-cff-interp-dict-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_OT_CFF_COMMON_PRIVATE_HH
+#define HB_OT_CFF_COMMON_PRIVATE_HH
+
++#include "hb-open-type.hh"
++#include "hb-ot-layout-common.hh"
++#include "hb-cff-interp-dict-common.hh"
+#include "hb-subset-plan.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+/* utility macro */
+template<typename Type>
+static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
+{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
+
+inline unsigned int calcOffSize(unsigned int offset)
+{
+ unsigned int size = 1;
+ while ((offset & ~0xFF) != 0)
+ {
+ size++;
+ offset >>= 8;
+ }
+ assert (size <= 4);
+ return size;
+}
+
+/* CFF INDEX */
+template <typename COUNT>
+struct CFFIndex
+{
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
+ (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+ c->check_array (offsets, offSize, count + 1) &&
+ c->check_array (data_base (), 1, max_offset () - 1))));
+ }
+
+ inline static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
+ { return offSize * (count + 1); }
+
+ inline unsigned int offset_array_size (void) const
+ { return calculate_offset_array_size (offSize, count); }
+
+ inline static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
+ { return min_size + calculate_offset_array_size (offSize, count) + dataSize; }
+
+ inline bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size ();
+ CFFIndex *dest = c->allocate_size<CFFIndex> (size);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ inline bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const hb_vector_t<ByteStr> &byteArray)
+ {
+ TRACE_SERIALIZE (this);
+ /* serialize CFFIndex header */
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count.set (byteArray.len);
+ this->offSize.set (offSize_);
+ if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
+ return_trace (false);
+
+ /* serialize indices */
+ unsigned int offset = 1;
+ unsigned int i = 0;
+ for (; i < byteArray.len; i++)
+ {
+ set_offset_at (i, offset);
+ offset += byteArray[i].get_size ();
+ }
+ set_offset_at (i, offset);
+
+ /* serialize data */
+ for (unsigned int i = 0; i < byteArray.len; i++)
+ {
+ ByteStr *dest = c->start_embed<ByteStr> ();
+ if (unlikely (dest == nullptr ||
+ !dest->serialize (c, byteArray[i])))
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ inline void set_offset_at (unsigned int index, unsigned int offset)
+ {
+ HBUINT8 *p = offsets + offSize * index + offSize;
+ unsigned int size = offSize;
+ for (; size; size--)
+ {
+ --p;
+ p->set (offset & 0xFF);
+ offset >>= 8;
+ }
+ }
+
+ inline const unsigned int offset_at (unsigned int index) const
+ {
+ assert (index <= count);
+ const HBUINT8 *p = offsets + offSize * index;
+ unsigned int size = offSize;
+ unsigned int offset = 0;
+ for (; size; size--)
+ offset = (offset << 8) + *p++;
+ return offset;
+ }
+
+ inline const unsigned int length_at (unsigned int index) const
+ { return offset_at (index + 1) - offset_at (index); }
+
+ inline const char *data_base (void) const
+ { return (const char *)this + min_size + offset_array_size (); }
+
+ inline unsigned int data_size (void) const
+ { return HBINT8::static_size; };
+
+ ByteStr operator [] (unsigned int index) const
+ {
+ if (likely (index < count))
+ return ByteStr (data_base () + offset_at (index) - 1, offset_at (index + 1) - offset_at (index));
+ else
+ return Null(ByteStr);
+ }
+
+ inline unsigned int get_size (void) const
+ {
+ if (this != &Null(CFFIndex))
+ {
+ if (count > 0)
+ return min_size + offset_array_size () + (offset_at (count) - 1);
+ else
+ return count.static_size; /* empty CFFIndex contains count only */
+ }
+ else
+ return 0;
+ }
+
+ protected:
+ inline unsigned int max_offset (void) const
+ {
+ unsigned int max = 0;
+ for (unsigned int i = 0; i <= count; i++)
+ {
+ unsigned int off = offset_at (i);
+ if (off > max) max = off;
+ }
+ return max;
+ }
+
+ public:
+ COUNT count; /* Number of object data. Note there are (count+1) offsets */
+ HBUINT8 offSize; /* The byte size of each offset in the offsets array. */
+ HBUINT8 offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
+ /* HBUINT8 data[VAR]; Object data */
+ public:
+ DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+};
+
+template <typename COUNT, typename TYPE>
+struct IndexOf : CFFIndex<COUNT>
+{
+ inline const ByteStr operator [] (unsigned int index) const
+ {
+ if (likely (index < CFFIndex<COUNT>::count))
+ return ByteStr (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
+ return Null(ByteStr);
+ }
+
+ template <typename DATA, typename PARAM1, typename PARAM2>
+ inline bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const hb_vector_t<DATA> &dataArray,
+ const hb_vector_t<unsigned int> &dataSizeArray,
+ const PARAM1 ¶m1,
+ const PARAM2 ¶m2)
+ {
+ TRACE_SERIALIZE (this);
+ /* serialize CFFIndex header */
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count.set (dataArray.len);
+ this->offSize.set (offSize_);
+ if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArray.len + 1))))
+ return_trace (false);
+
+ /* serialize indices */
+ unsigned int offset = 1;
+ unsigned int i = 0;
+ for (; i < dataArray.len; i++)
+ {
+ CFFIndex<COUNT>::set_offset_at (i, offset);
+ offset += dataSizeArray[i];
+ }
+ CFFIndex<COUNT>::set_offset_at (i, offset);
+
+ /* serialize data */
+ for (unsigned int i = 0; i < dataArray.len; i++)
+ {
+ TYPE *dest = c->start_embed<TYPE> ();
+ if (unlikely (dest == nullptr ||
+ !dest->serialize (c, dataArray[i], param1, param2)))
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ /* in parallel to above */
+ template <typename DATA, typename PARAM>
+ inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
+ const hb_vector_t<DATA> &dataArray,
+ hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
+ const PARAM ¶m)
+ {
+ /* determine offset size */
+ unsigned int totalDataSize = 0;
+ for (unsigned int i = 0; i < dataArray.len; i++)
+ {
+ unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
+ dataSizeArray[i] = dataSize;
+ totalDataSize += dataSize;
+ }
+ offSize_ = calcOffSize (totalDataSize);
+
+ return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArray.len, totalDataSize);
+ }
+};
+
+/* Top Dict, Font Dict, Private Dict */
+struct Dict : UnsizedByteStr
+{
+ template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
+ inline bool serialize (hb_serialize_context_t *c,
+ const DICTVAL &dictval,
+ OP_SERIALIZER& opszr,
+ PARAM& param)
+ {
+ TRACE_SERIALIZE (this);
+ for (unsigned int i = 0; i < dictval.values.len; i++)
+ {
+ if (unlikely (!opszr.serialize (c, dictval.values[i], param)))
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ /* in parallel to above */
+ template <typename DICTVAL, typename OP_SERIALIZER>
+ inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
+ OP_SERIALIZER& opszr)
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < dictval.values.len; i++)
+ size += opszr.calculate_serialized_size (dictval.values[i]);
+ return size;
+ }
+
+ template <typename INTTYPE, int minVal, int maxVal>
+ inline static bool serialize_offset_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
+ {
+ if (value == 0)
+ return true;
+ // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
+ if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
+ return false;
+
+ TRACE_SERIALIZE (this);
+ /* serialize the opcode */
+ HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
+ if (unlikely (p == nullptr)) return_trace (false);
+ if (Is_OpCode_ESC (op))
+ {
+ p->set (OpCode_escape);
+ op = Unmake_OpCode_ESC (op);
+ p++;
+ }
+ p->set (op);
+ return_trace (true);
+ }
+
+ inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
+ { return serialize_offset_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
+
+ inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
+ { return serialize_offset_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
+};
+
+struct TopDict : Dict {};
+struct FontDict : Dict {};
+struct PrivateDict : Dict {};
+
+struct TableInfo
+{
+ void init (void) { offSize = offset = size = 0; }
+
+ unsigned int offset;
+ unsigned int size;
+ unsigned int offSize;
+};
+
+/* font dict index remap table from fullset FDArray to subset FDArray.
+ * set to HB_SET_VALUE_INVALID if excluded from subset */
+struct FDMap : hb_vector_t<hb_codepoint_t>
+{
+ inline void init (void)
+ { hb_vector_t<hb_codepoint_t>::init (); }
+
+ inline void fini (void)
+ { hb_vector_t<hb_codepoint_t>::fini (); }
+
+ inline bool fullset (void) const
+ {
+ for (unsigned int i = 0; i < len; i++)
+ if (hb_vector_t<hb_codepoint_t>::operator[] (i) == HB_SET_VALUE_INVALID)
+ return false;
+ return true;
+ }
+
+ inline bool excludes (hb_codepoint_t fd) const
+ { return (fd < len) && ((*this)[fd] == HB_SET_VALUE_INVALID); }
+
+ inline hb_codepoint_t operator[] (hb_codepoint_t i) const
+ {
+ if (fullset ())
+ return i;
+ else
+ return hb_vector_t<hb_codepoint_t>::operator[] (i);
+ }
+
+ inline hb_codepoint_t &operator[] (hb_codepoint_t i)
+ {
+ assert (i < len);
+ return hb_vector_t<hb_codepoint_t>::operator[] (i);
+ }
+};
+
+template <typename COUNT>
+struct FDArray : IndexOf<COUNT, FontDict>
+{
+ template <typename DICTVAL, typename OP_SERIALIZER>
+ inline bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const hb_vector_t<DICTVAL> &fontDicts,
+ unsigned int fdCount,
+ const FDMap &fdmap,
+ OP_SERIALIZER& opszr,
+ const hb_vector_t<TableInfo> &privateInfos)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count.set (fdCount);
+ this->offSize.set (offSize_);
+ if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
+ return_trace (false);
+
+ /* serialize font dict offsets */
+ unsigned int offset = 1;
+ unsigned int fid = 0;
+ for (unsigned i = 0; i < fontDicts.len; i++)
+ if (!fdmap.excludes (i))
+ {
+ IndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
+ offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+ }
+ IndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+
+ /* serialize font dicts */
+ for (unsigned int i = 0; i < fontDicts.len; i++)
+ if (!fdmap.excludes (i))
+ {
+ FontDict *dict = c->start_embed<FontDict> ();
+ if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+
+ /* in parallel to above */
+ template <typename OP_SERIALIZER, typename DICTVAL>
+ inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
+ const hb_vector_t<DICTVAL> &fontDicts,
+ unsigned int fdCount,
+ const FDMap &fdmap,
+ OP_SERIALIZER& opszr)
+ {
+ unsigned int dictsSize = 0;
+ for (unsigned int i = 0; i < fontDicts.len; i++)
+ if (!fdmap.excludes (i))
+ dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+
+ offSize_ = calcOffSize (dictsSize + 1);
+ return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
+ }
+};
+
+/* FDSelect */
+struct FDSelect0 {
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!(c->check_struct (this))))
+ return_trace (false);
+ for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
+ if (unlikely (!fds[i].sanitize (c)))
+ return_trace (false);
+
+ return_trace (true);
+ }
+
+ inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ return (hb_codepoint_t)fds[glyph];
+ }
+
+ inline unsigned int get_size (unsigned int num_glyphs) const
+ { return HBUINT8::static_size * num_glyphs; }
+
+ HBUINT8 fds[VAR];
+
+ DEFINE_SIZE_MIN (1);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4_Range {
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
+ }
+
+ GID_TYPE first;
+ FD_TYPE fd;
+
+ DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4 {
+ inline unsigned int get_size (void) const
+ { return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
+
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
+ return_trace (false);
+
+ for (unsigned int i = 0; i < nRanges; i++)
+ {
+ if (unlikely (!ranges[i].sanitize (c, fdcount)))
+ return_trace (false);
+ if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
+ return_trace (false);
+ }
+ if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
+ return_trace (false);
+
+ return_trace (true);
+ }
+
+ inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ for (unsigned int i = 1; i < nRanges; i++)
+ if (glyph < ranges[i].first)
+ return (hb_codepoint_t)ranges[i - 1].fd;
+
+ assert (false);
+ }
+
+ inline GID_TYPE &sentinel (void) { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+ inline const GID_TYPE &sentinel (void) const { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+
+ GID_TYPE nRanges;
+ FDSelect3_4_Range<GID_TYPE, FD_TYPE> ranges[VAR];
+ /* GID_TYPE sentinel */
+
+ DEFINE_SIZE_ARRAY (GID_TYPE::static_size * 2, ranges);
+};
+
+typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
+typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
+
+struct FDSelect {
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+
+ return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
+ (format == 0)?
+ u.format0.sanitize (c, fdcount):
+ u.format3.sanitize (c, fdcount)));
+ }
+
+ inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size (num_glyphs);
+ FDSelect *dest = c->allocate_size<FDSelect> (size);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+ { return get_size (num_glyphs); }
+
+ inline unsigned int get_size (unsigned int num_glyphs) const
+ {
+ unsigned int size = format.static_size;
+ if (format == 0)
+ size += u.format0.get_size (num_glyphs);
+ else
+ size += u.format3.get_size ();
+ return size;
+ }
+
+ inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ if (this == &Null(FDSelect))
+ return 0;
+ if (format == 0)
+ return u.format0.get_fd (glyph);
+ else
+ return u.format3.get_fd (glyph);
+ }
+
+ HBUINT8 format;
+ union {
+ FDSelect0 format0;
+ FDSelect3 format3;
+ } u;
+
+ DEFINE_SIZE_MIN (1);
+};
+
+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);
+ }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_OT_CFF_COMMON_PRIVATE_HH */
+
--- /dev/null
- #include "hb-ot-cff-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_CFF1_TABLE_HH
+#define HB_OT_CFF1_TABLE_HH
+
++#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff1.hh"
+
+namespace CFF {
+
+/*
+ * CFF -- Compact Font Format (CFF)
+ * http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf
+ */
+#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ')
+
+typedef CFFIndex<HBUINT16> CFF1Index;
+template <typename Type> struct CFF1IndexOf : IndexOf<HBUINT16, Type> {};
+
+typedef CFFIndex<HBUINT16> CFF1Index;
+typedef CFF1Index CFF1CharStrings;
+typedef FDArray<HBUINT16> CFF1FDArray;
+typedef Subrs<HBUINT16> CFF1Subrs;
+
+struct CFF1FDSelect : FDSelect {};
+
+/* Encoding */
+struct Encoding0 {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c));
+ }
+
+ inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
+ {
+ if (glyph < nCodes)
+ {
+ code = (hb_codepoint_t)codes[glyph];
+ return true;
+ }
+ else
+ return false;
+ }
+
+ inline unsigned int get_size (void) const
+ { return HBUINT8::static_size * (nCodes + 1); }
+
+ HBUINT8 nCodes;
+ HBUINT8 codes[VAR];
+
+ DEFINE_SIZE_ARRAY(1, codes);
+};
+
+struct Encoding1_Range {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ HBUINT8 first;
+ HBUINT8 nLeft;
+
+ DEFINE_SIZE_STATIC (2);
+};
+
+struct Encoding1 {
+ inline unsigned int get_size (void) const
+ { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; }
+
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
+ }
+
+ inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
+ {
+ for (unsigned int i = 0; i < nRanges; i++)
+ {
+ if (glyph <= ranges[i].nLeft)
+ {
+ code = (hb_codepoint_t)ranges[i].first + glyph;
+ return true;
+ }
+ glyph -= (ranges[i].nLeft + 1);
+ }
+
+ return false;
+ }
+
+ HBUINT8 nRanges;
+ Encoding1_Range ranges[VAR];
+
+ DEFINE_SIZE_ARRAY (1, ranges);
+};
+
+struct EncSupplement {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ HBUINT8 code;
+ HBUINT16 glyph;
+
+ DEFINE_SIZE_STATIC (3);
+};
+
+struct CFF1SuppEncData {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c)));
+ }
+
+ inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
+ {
+ for (unsigned int i = 0; i < nSups; i++)
+ if (glyph == supps[i].glyph)
+ {
+ code = supps[i].code;
+ return true;
+ }
+
+ return false;
+ }
+
+ inline unsigned int get_size (void) const
+ { return HBUINT8::static_size + EncSupplement::static_size * nSups; }
+
+ HBUINT8 nSups;
+ EncSupplement supps[VAR];
+
+ DEFINE_SIZE_ARRAY (1, supps);
+};
+
+struct Encoding {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+ unsigned int fmt = format & 0x7F;
+ if (unlikely (fmt > 1))
+ return_trace (false);
+ if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c))))
+ return_trace (false);
+ return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c));
+ }
+
+ inline bool serialize (hb_serialize_context_t *c, const Encoding &src, unsigned int num_glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size ();
+ Encoding *dest = c->allocate_size<Encoding> (size);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (void) const
+ { return get_size (); } // XXX: TODO
+
+ inline unsigned int get_size (void) const
+ {
+ unsigned int size = min_size;
+ if ((format & 0x7F) == 0)
+ size += u.format0.get_size ();
+ else
+ size += u.format1.get_size ();
+ if ((format & 0x80) != 0)
+ size += suppEncData ().get_size ();
+ return size;
+ }
+
+ inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
+ {
+ // XXX: TODO
+ return false;
+ }
+
+ inline const CFF1SuppEncData &suppEncData (void) const
+ {
+ if ((format & 0x7F) == 0)
+ return StructAfter<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes-1]);
+ else
+ return StructAfter<CFF1SuppEncData> (u.format1.ranges[u.format1.nRanges-1]);
+ }
+
+ HBUINT8 format;
+ union {
+ Encoding0 format0;
+ Encoding1 format1;
+ } u;
+ /* CFF1SuppEncData suppEncData; */
+
+ DEFINE_SIZE_MIN (1);
+};
+
+/* Charset */
+struct Charset0 {
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+ }
+
+ inline unsigned int get_sid (hb_codepoint_t glyph)
+ {
+ if (glyph == 0)
+ return 0;
+ else
+ return sids[glyph - 1];
+ }
+
+ inline unsigned int get_size (unsigned int num_glyphs) const
+ {
+ assert (num_glyphs > 0);
+ return HBUINT16::static_size * (num_glyphs - 1);
+ }
+
+ HBUINT16 sids[VAR];
+
+ DEFINE_SIZE_ARRAY(0, sids);
+};
+
+template <typename TYPE>
+struct Charset_Range {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ HBUINT8 first;
+ TYPE nLeft;
+
+ DEFINE_SIZE_STATIC (1 + TYPE::static_size);
+};
+
+template <typename TYPE>
+struct Charset1_2 {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
+ }
+
+ inline bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const
+ {
+ for (unsigned int i = 0; i < nRanges; i++)
+ {
+ if (glyph <= ranges[i].nLeft)
+ {
+ sid = (hb_codepoint_t)ranges[i].first + glyph;
+ return true;
+ }
+ glyph -= (ranges[i].nLeft + 1);
+ }
+
+ return false;
+ }
+
+ inline unsigned int get_size (void) const
+ { return HBUINT8::static_size + Charset_Range<TYPE>::static_size * nRanges; }
+
+ HBUINT8 nRanges;
+ Charset_Range<TYPE> ranges[VAR];
+
+ DEFINE_SIZE_ARRAY (1, ranges);
+};
+
+struct Charset {
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+ if (format == 0)
+ return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
+ else if (format == 1)
+ return_trace (u.format1.sanitize (c));
+ else if (likely (format == 2))
+ return_trace (u.format2.sanitize (c));
+ else
+ return_trace (false);
+ }
+
+ inline bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size (num_glyphs);
+ Charset *dest = c->allocate_size<Charset> (size);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+ { return get_size (num_glyphs); } // XXX: TODO
+
+ inline unsigned int get_size (unsigned int num_glyphs) const
+ {
+ unsigned int size = min_size;
+ if (format == 0)
+ size += u.format0.get_size (num_glyphs);
+ else if (format == 1)
+ size += u.format1.get_size ();
+ else
+ size += u.format2.get_size ();
+ return size;
+ }
+
+ inline bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const
+ {
+ // XXX: TODO
+ return false;
+ }
+
+ HBUINT8 format;
+ union {
+ Charset0 format0;
+ Charset1_2<HBUINT8> format1;
+ Charset1_2<HBUINT16> format2;
+ } u;
+
+ DEFINE_SIZE_MIN (1);
+};
+
+struct CFF1TopDictValues : TopDictValues
+{
+ inline void init (void)
+ {
+ TopDictValues::init ();
+
+ ros[0] = ros[1] = ros[2] = 0;
+ cidCount = 8720;
+ EncodingOffset = 0;
+ CharsetOffset = 0;
+ FDSelectOffset = 0;
+ privateDictInfo.init ();
+ }
+
+ inline void fini (void)
+ {
+ TopDictValues::fini ();
+ }
+
+ inline bool is_CID (void) const
+ { return ros[0] != 0; }
+
+ inline unsigned int calculate_serialized_size (void) const
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < values.len; i++)
+ {
+ OpCode op = values[i].op;
+ switch (op)
+ {
+ case OpCode_FDSelect:
+ size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+ break;
+ default:
+ size += TopDictValues::calculate_serialized_op_size (values[i]);
+ break;
+ }
+ }
+ return size;
+ }
+
+ unsigned int ros[3]; /* registry, ordering, supplement */
+ unsigned int cidCount;
+
+ enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
+ unsigned int EncodingOffset;
+ unsigned int CharsetOffset;
+ unsigned int FDSelectOffset;
+ TableInfo privateDictInfo;
+};
+
+struct CFF1TopDictOpSet : TopDictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF1TopDictValues& dictval)
+ {
+
+ switch (op) {
+ case OpCode_version:
+ case OpCode_Notice:
+ case OpCode_Copyright:
+ case OpCode_FullName:
+ case OpCode_FamilyName:
+ case OpCode_Weight:
+ case OpCode_isFixedPitch:
+ case OpCode_ItalicAngle:
+ case OpCode_UnderlinePosition:
+ case OpCode_UnderlineThickness:
+ case OpCode_PaintType:
+ case OpCode_CharstringType:
+ case OpCode_UniqueID:
+ case OpCode_StrokeWidth:
+ case OpCode_SyntheticBase:
+ case OpCode_PostScript:
+ case OpCode_BaseFontName:
+ case OpCode_CIDFontVersion:
+ case OpCode_CIDFontRevision:
+ case OpCode_CIDFontType:
+ case OpCode_UIDBase:
+ case OpCode_FontBBox:
+ case OpCode_XUID:
+ case OpCode_BaseFontBlend:
+ env.argStack.clear ();
+ break;
+
+ case OpCode_CIDCount:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.cidCount)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ case OpCode_ROS:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.ros[2]) ||
+ !env.argStack.check_pop_uint (dictval.ros[1]) ||
+ !env.argStack.check_pop_uint (dictval.ros[0])))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ case OpCode_Encoding:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.EncodingOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ case OpCode_charset:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.CharsetOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ case OpCode_FDSelect:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ case OpCode_Private:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+ return false;
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!TopDictOpSet::process_op (op, env, dictval)))
+ return false;
+ /* Record this operand below if stack is empty, otherwise done */
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+struct CFF1FontDictValues : DictValues<OpStr>
+{
+ inline void init (void)
+ {
+ DictValues<OpStr>::init ();
+ privateDictInfo.init ();
+ }
+
+ inline void fini (void)
+ {
+ DictValues<OpStr>::fini ();
+ }
+
+ TableInfo privateDictInfo;
+};
+
+struct CFF1FontDictOpSet : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF1FontDictValues& dictval)
+ {
+ switch (op) {
+ case OpCode_FontName:
+ case OpCode_FontMatrix:
+ case OpCode_PaintType:
+ env.argStack.clear ();
+ break;
+ case OpCode_Private:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+ return false;
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+template <typename VAL>
+struct CFF1PrivateDictValues_Base : DictValues<VAL>
+{
+ inline void init (void)
+ {
+ DictValues<VAL>::init ();
+ subrsOffset = 0;
+ localSubrs = &Null(CFF1Subrs);
+ }
+
+ inline void fini (void)
+ {
+ DictValues<VAL>::fini ();
+ }
+
+ inline unsigned int calculate_serialized_size (void) const
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < DictValues<VAL>::values.len; i++)
+ if (DictValues<VAL>::values[i].op == OpCode_Subrs)
+ size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+ else
+ size += DictValues<VAL>::values[i].str.len;
+ return size;
+ }
+
+ unsigned int subrsOffset;
+ const CFF1Subrs *localSubrs;
+};
+
+typedef CFF1PrivateDictValues_Base<OpStr> CFF1PrivateDictValues_Subset;
+typedef CFF1PrivateDictValues_Base<DictVal> CFF1PrivateDictValues;
+
+struct CFF1PrivateDictOpSet : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF1PrivateDictValues& dictval)
+ {
+ DictVal val;
+ val.init ();
+
+ switch (op) {
+ case OpCode_BlueValues:
+ case OpCode_OtherBlues:
+ case OpCode_FamilyBlues:
+ case OpCode_FamilyOtherBlues:
+ case OpCode_StemSnapH:
+ case OpCode_StemSnapV:
+ if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
+ return false;
+ break;
+ case OpCode_StdHW:
+ case OpCode_StdVW:
+ case OpCode_BlueScale:
+ case OpCode_BlueShift:
+ case OpCode_BlueFuzz:
+ case OpCode_ForceBold:
+ case OpCode_LanguageGroup:
+ case OpCode_ExpansionFactor:
+ case OpCode_initialRandomSeed:
+ case OpCode_defaultWidthX:
+ case OpCode_nominalWidthX:
+ if (unlikely (!env.argStack.check_pop_num (val.single_val)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_Subrs:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr, val);
+ return true;
+ }
+};
+
+struct CFF1PrivateDictOpSet_Subset : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF1PrivateDictValues_Subset& dictval)
+ {
+ switch (op) {
+ case OpCode_BlueValues:
+ case OpCode_OtherBlues:
+ case OpCode_FamilyBlues:
+ case OpCode_FamilyOtherBlues:
+ case OpCode_StemSnapH:
+ case OpCode_StemSnapV:
+ case OpCode_StdHW:
+ case OpCode_StdVW:
+ case OpCode_BlueScale:
+ case OpCode_BlueShift:
+ case OpCode_BlueFuzz:
+ case OpCode_ForceBold:
+ case OpCode_LanguageGroup:
+ case OpCode_ExpansionFactor:
+ case OpCode_initialRandomSeed:
+ case OpCode_defaultWidthX:
+ case OpCode_nominalWidthX:
+ env.argStack.clear ();
+ break;
+
+ case OpCode_Subrs:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+typedef DictInterpreter<CFF1TopDictOpSet, CFF1TopDictValues> CFF1TopDict_Interpreter;
+typedef DictInterpreter<CFF1FontDictOpSet, CFF1FontDictValues> CFF1FontDict_Interpreter;
+typedef DictInterpreter<CFF1PrivateDictOpSet, CFF1PrivateDictValues> CFF1PrivateDict_Interpreter;
+
+typedef CFF1Index CFF1NameIndex;
+typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
+typedef CFF1Index CFF1StringIndex;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff1
+{
+ static const hb_tag_t tableTag = HB_OT_TAG_cff1;
+
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ likely (version.major == 1));
+ }
+
+ template <typename PrivOpSet, typename PrivDictVal>
+ struct accelerator_templ_t
+ {
+ inline void init (hb_face_t *face)
+ {
+ topDicts.init ();
+ topDicts.resize (1);
+ topDicts[0].init ();
+ fontDicts.init ();
+ privateDicts.init ();
+
+ this->blob = sc.reference_table<cff1> (face);
+
+ /* setup for run-time santization */
+ sc.init (this->blob);
+ sc.start_processing ();
+
+ const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
+
+ if (cff == &Null(OT::cff1))
+ { fini (); return; }
+
+ nameIndex = &cff->nameIndex (cff);
+ if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
+ { fini (); return; }
+
+ topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
+ if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
+ { fini (); return; }
+
+ { /* parse top dict */
+ const ByteStr topDictStr = (*topDictIndex)[0];
+ if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ CFF1TopDict_Interpreter top_interp;
+ top_interp.env.init (topDictStr);
+ if (unlikely (!top_interp.interpret (topDicts[0]))) { fini (); return; }
+ }
+
+ encoding = &Null(Encoding);
+ charset = &StructAtOffsetOrNull<Charset> (cff, topDicts[0].CharsetOffset);
+
+ fdCount = 1;
+ if (is_CID ())
+ {
+ fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDicts[0].FDArrayOffset);
+ fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDicts[0].FDSelectOffset);
+ if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) ||
+ (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
+ { fini (); return; }
+
+ fdCount = fdArray->count;
+ }
+ else
+ {
+ fdArray = &Null(CFF1FDArray);
+ fdSelect = &Null(CFF1FDSelect);
+ if (topDicts[0].EncodingOffset != CFF1TopDictValues::StandardEncoding &&
+ topDicts[0].EncodingOffset != CFF1TopDictValues::ExpertEncoding)
+ {
+ encoding = &StructAtOffsetOrNull<Encoding> (cff, topDicts[0].EncodingOffset);
+ if ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))
+ { fini (); return; }
+ }
+ }
+
+ stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
+ if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
+ { fini (); return; }
+
+ globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
+ if ((globalSubrs != &Null (CFF1Subrs)) && !stringIndex->sanitize (&sc))
+ { fini (); return; }
+
+ charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDicts[0].charStringsOffset);
+
+ if ((charStrings == &Null(CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
+ { fini (); return; }
+
+ num_glyphs = charStrings->count;
+ if (num_glyphs != sc.get_num_glyphs ())
+ { fini (); return; }
+
+ privateDicts.resize (fdCount);
+ for (unsigned int i = 0; i < fdCount; i++)
+ privateDicts[i].init ();
+
+ // parse CID font dicts and gather private dicts
+ if (is_CID ())
+ {
+ for (unsigned int i = 0; i < fdCount; i++)
+ {
+ ByteStr fontDictStr = (*fdArray)[i];
+ if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ CFF1FontDictValues *font;
+ CFF1FontDict_Interpreter font_interp;
+ font_interp.env.init (fontDictStr);
+ font = fontDicts.push ();
+ 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);
+ if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+ priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+ if (priv->localSubrs != &Null(CFF1Subrs) &&
+ unlikely (!priv->localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+ else /* non-CID */
+ {
+ CFF1TopDictValues *font = &topDicts[0];
+ PrivDictVal *priv = &privateDicts[0];
+
+ 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);
+ if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+ priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+ if (priv->localSubrs != &Null(CFF1Subrs) &&
+ unlikely (!priv->localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+
+ inline void fini (void)
+ {
+ sc.end_processing ();
+ topDicts[0].fini ();
+ topDicts.fini ();
+ fontDicts.fini ();
+ privateDicts.fini ();
+ hb_blob_destroy (blob);
+ blob = nullptr;
+ }
+
+ inline bool is_valid (void) const { return blob != nullptr; }
+ inline bool is_CID (void) const { return topDicts[0].is_CID (); }
+
+ inline bool get_extents (hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents) const
+ {
+ // XXX: TODO
+ if (glyph >= num_glyphs)
+ return false;
+
+ return true;
+ }
+
+ protected:
+ hb_blob_t *blob;
+ hb_sanitize_context_t sc;
+
+ public:
+ const CFF1NameIndex *nameIndex;
+ const CFF1TopDictIndex *topDictIndex;
+ const CFF1StringIndex *stringIndex;
+ const Encoding *encoding;
+ const Charset *charset;
+ const CFF1Subrs *globalSubrs;
+ const CFF1CharStrings *charStrings;
+ const CFF1FDArray *fdArray;
+ const CFF1FDSelect *fdSelect;
+ unsigned int fdCount;
+
+ hb_vector_t<CFF1TopDictValues> topDicts;
+ hb_vector_t<CFF1FontDictValues> fontDicts;
+ hb_vector_t<PrivDictVal> privateDicts;
+
+ unsigned int num_glyphs;
+ };
+
+ typedef accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues> accelerator_t;
+ typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> accelerator_subset_t;
+
+ inline bool subset (hb_subset_plan_t *plan) const
+ {
+ hb_blob_t *cff_prime = nullptr;
+
+ bool success = true;
+ if (hb_subset_cff1 (plan, &cff_prime)) {
+ success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime);
+ } else {
+ success = false;
+ }
+ hb_blob_destroy (cff_prime);
+
+ return success;
+ }
+
+ public:
+ FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */
+ OffsetTo<CFF1NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */
+ HBUINT8 offSize; /* offset size (unused?) */
+
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF1_TABLE_HH */
--- /dev/null
- #include "hb-ot-cff-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_CFF2_TABLE_HH
+#define HB_OT_CFF2_TABLE_HH
+
++#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff2.hh"
+
+namespace CFF {
+
+/*
+ * CFF2 -- Compact Font Format (CFF) Version 2
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
+ */
+#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
+
+typedef CFFIndex<HBUINT32> CFF2Index;
+template <typename Type> struct CFF2IndexOf : IndexOf<HBUINT32, Type> {};
+
+typedef CFF2Index CFF2CharStrings;
+typedef FDArray<HBUINT32> CFF2FDArray;
+typedef Subrs<HBUINT32> CFF2Subrs;
+
+typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
+typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
+
+struct CFF2FDSelect
+{
+ inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+
+ return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) &&
+ (format == 0)?
+ u.format0.sanitize (c, fdcount):
+ ((format == 3)?
+ u.format3.sanitize (c, fdcount):
+ u.format4.sanitize (c, fdcount))));
+ }
+
+ inline bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size (num_glyphs);
+ CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+ { return get_size (num_glyphs); }
+
+ inline unsigned int get_size (unsigned int num_glyphs) const
+ {
+ unsigned int size = format.static_size;
+ if (format == 0)
+ size += u.format0.get_size (num_glyphs);
+ else if (format == 3)
+ size += u.format3.get_size ();
+ else
+ size += u.format4.get_size ();
+ return size;
+ }
+
+ inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ if (this == &Null(CFF2FDSelect))
+ return 0;
+ if (format == 0)
+ return u.format0.get_fd (glyph);
+ else if (format == 3)
+ return u.format3.get_fd (glyph);
+ else
+ return u.format4.get_fd (glyph);
+ }
+
+ HBUINT8 format;
+ union {
+ FDSelect0 format0;
+ FDSelect3 format3;
+ FDSelect4 format4;
+ } u;
+
+ DEFINE_SIZE_MIN (2);
+};
+
+struct CFF2VariationStore
+{
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this)) && varStore.sanitize (c));
+ }
+
+ inline bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size_ = varStore->get_size ();
+ CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
+ if (unlikely (dest == nullptr)) return_trace (false);
+ memcpy (dest, varStore, size_);
+ return_trace (true);
+ }
+
+ inline unsigned int get_size (void) const { return HBUINT16::static_size + size; }
+
+ HBUINT16 size;
+ VariationStore varStore;
+
+ DEFINE_SIZE_MIN (2 + VariationStore::min_size);
+};
+
+struct CFF2TopDictValues : TopDictValues
+{
+ inline void init (void)
+ {
+ TopDictValues::init ();
+ vstoreOffset = 0;
+ FDSelectOffset = 0;
+ }
+
+ inline void fini (void)
+ {
+ TopDictValues::fini ();
+ }
+
+ inline unsigned int calculate_serialized_size (void) const
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < values.len; i++)
+ {
+ OpCode op = values[i].op;
+ switch (op)
+ {
+ case OpCode_vstore:
+ case OpCode_FDSelect:
+ size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+ break;
+ default:
+ size += TopDictValues::calculate_serialized_op_size (values[i]);
+ break;
+ }
+ }
+ return size;
+ }
+
+ unsigned int vstoreOffset;
+ unsigned int FDSelectOffset;
+};
+
+struct CFF2TopDictOpSet : TopDictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF2TopDictValues& dictval)
+ {
+ switch (op) {
+ case OpCode_FontMatrix:
+ {
+ DictVal val;
+ val.init ();
+ dictval.pushVal (op, env.substr);
+ env.argStack.clear ();
+ }
+ break;
+
+ case OpCode_vstore:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.vstoreOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_FDSelect:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!TopDictOpSet::process_op (op, env, dictval)))
+ return false;
+ /* Record this operand below if stack is empty, otherwise done */
+ if (!env.argStack.is_empty ()) return true;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+struct CFF2FontDictValues : DictValues<OpStr>
+{
+ inline void init (void)
+ {
+ DictValues<OpStr>::init ();
+ privateDictInfo.init ();
+ }
+
+ inline void fini (void)
+ {
+ DictValues<OpStr>::fini ();
+ }
+
+ TableInfo privateDictInfo;
+};
+
+struct CFF2FontDictOpSet : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF2FontDictValues& dictval)
+ {
+ switch (op) {
+ case OpCode_Private:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+ return false;
+ if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ())
+ return true;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+template <typename VAL>
+struct CFF2PrivateDictValues_Base : DictValues<VAL>
+{
+ inline void init (void)
+ {
+ DictValues<VAL>::init ();
+ subrsOffset = 0;
+ localSubrs = &Null(CFF2Subrs);
+ }
+
+ inline void fini (void)
+ {
+ DictValues<VAL>::fini ();
+ }
+
+ inline unsigned int calculate_serialized_size (void) const
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < DictValues<VAL>::values.len; i++)
+ if (DictValues<VAL>::values[i].op == OpCode_Subrs)
+ size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+ else
+ size += DictValues<VAL>::values[i].str.len;
+ return size;
+ }
+
+ unsigned int subrsOffset;
+ const CFF2Subrs *localSubrs;
+};
+
+typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
+typedef CFF2PrivateDictValues_Base<DictVal> CFF2PrivateDictValues;
+
+struct CFF2PrivateDictOpSet : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF2PrivateDictValues& dictval)
+ {
+ DictVal val;
+ val.init ();
+
+ switch (op) {
+ case OpCode_StdHW:
+ case OpCode_StdVW:
+ case OpCode_BlueScale:
+ case OpCode_BlueShift:
+ case OpCode_BlueFuzz:
+ case OpCode_ExpansionFactor:
+ case OpCode_LanguageGroup:
+ if (unlikely (!env.argStack.check_pop_num (val.single_val)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_BlueValues:
+ case OpCode_OtherBlues:
+ case OpCode_FamilyBlues:
+ case OpCode_FamilyOtherBlues:
+ case OpCode_StemSnapH:
+ case OpCode_StemSnapV:
+ if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_Subrs:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+ case OpCode_vsindexdict:
+ case OpCode_blenddict:
+ // XXX: TODO
+ return true;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr, val);
+ return true;
+ }
+};
+
+struct CFF2PrivateDictOpSet_Subset : DictOpSet
+{
+ static inline bool process_op (OpCode op, InterpEnv& env, CFF2PrivateDictValues_Subset& dictval)
+ {
+ switch (op) {
+ case OpCode_BlueValues:
+ case OpCode_OtherBlues:
+ case OpCode_FamilyBlues:
+ case OpCode_FamilyOtherBlues:
+ case OpCode_StdHW:
+ case OpCode_StdVW:
+ case OpCode_BlueScale:
+ case OpCode_BlueShift:
+ case OpCode_BlueFuzz:
+ case OpCode_StemSnapH:
+ case OpCode_StemSnapV:
+ case OpCode_LanguageGroup:
+ case OpCode_ExpansionFactor:
+ env.argStack.clear ();
+ break;
+
+ case OpCode_blenddict:
+ env.argStack.clear ();
+ return true;
+
+ case OpCode_Subrs:
+ if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+ return false;
+ env.argStack.clear ();
+ break;
+
+ default:
+ if (unlikely (!DictOpSet::process_op (op, env)))
+ return false;
+ if (!env.argStack.is_empty ()) return true;
+ break;
+ }
+
+ dictval.pushVal (op, env.substr);
+ return true;
+ }
+};
+
+typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+typedef DictInterpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff2
+{
+ static const hb_tag_t tableTag = HB_OT_TAG_cff2;
+
+ inline bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ likely (version.major == 2));
+ }
+
+ template <typename PrivOpSet, typename PrivDictVal>
+ struct accelerator_templ_t
+ {
+ inline void init (hb_face_t *face)
+ {
+ topDict.init ();
+ fontDicts.init ();
+ privateDicts.init ();
+
+ this->blob = sc.reference_table<cff2> (face);
+
+ /* setup for run-time santization */
+ sc.init (this->blob);
+ sc.start_processing ();
+
+ const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
+
+ if (cff2 == &Null(OT::cff2))
+ { fini (); return; }
+
+ { /* parse top dict */
+ ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+ if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ CFF2TopDict_Interpreter top_interp;
+ top_interp.env.init (topDictStr);
+ if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ }
+
+ globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
+ varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
+ charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
+ fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
+ fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);
+
+ if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
+ (charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
+ (fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
+ (((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
+ { fini (); return; }
+
+ num_glyphs = charStrings->count;
+ if (num_glyphs != sc.get_num_glyphs ())
+ { fini (); return; }
+
+ fdCount = fdArray->count;
+ privateDicts.resize (fdCount);
+
+ /* parse font dicts and gather private dicts */
+ for (unsigned int i = 0; i < fdCount; i++)
+ {
+ const ByteStr fontDictStr = (*fdArray)[i];
+ if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ CFF2FontDictValues *font;
+ CFF2FontDict_Interpreter font_interp;
+ font_interp.env.init (fontDictStr);
+ font = fontDicts.push ();
+ 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> priv_interp;
+ priv_interp.env.init(privDictStr);
+ if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
+
+ privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
+ if (privateDicts[i].localSubrs != &Null(CFF2Subrs) &&
+ unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+
+ inline void fini (void)
+ {
+ sc.end_processing ();
+ fontDicts.fini ();
+ privateDicts.fini ();
+ hb_blob_destroy (blob);
+ blob = nullptr;
+ }
+
+ inline bool is_valid (void) const { return blob != nullptr; }
+
+ inline bool get_extents (hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents) const
+ {
+ // XXX: TODO
+ if (glyph >= num_glyphs)
+ return false;
+
+ return true;
+ }
+
+ protected:
+ hb_blob_t *blob;
+ hb_sanitize_context_t sc;
+
+ public:
+ CFF2TopDictValues topDict;
+ const CFF2Subrs *globalSubrs;
+ const CFF2VariationStore *varStore;
+ const CFF2CharStrings *charStrings;
+ const CFF2FDArray *fdArray;
+ const CFF2FDSelect *fdSelect;
+ unsigned int fdCount;
+
+ hb_vector_t<CFF2FontDictValues> fontDicts;
+ hb_vector_t<PrivDictVal> privateDicts;
+
+ unsigned int num_glyphs;
+ };
+
+ typedef accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues> accelerator_t;
+ typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t;
+
+ inline bool subset (hb_subset_plan_t *plan) const
+ {
+ hb_blob_t *cff2_prime = nullptr;
+
+ bool success = true;
+ if (hb_subset_cff2 (plan, &cff2_prime)) {
+ success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime);
+ } else {
+ success = false;
+ }
+ hb_blob_destroy (cff2_prime);
+
+ return success;
+ }
+
+ public:
+ FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */
+ OffsetTo<TopDict, HBUINT8> topDict; /* headerSize = Offset to Top DICT. */
+ HBUINT16 topDictSize; /* Top DICT size */
+
+ public:
+ DEFINE_SIZE_STATIC (5);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-post-table.hh"
-
+ #include "hb-ot-glyf-table.hh"
++#include "hb-ot-cff1-table.hh"
++#include "hb-ot-cff2-table.hh"
#include "hb-ot-color-cbdt-table.hh"
--- /dev/null
- #include "hb-ot-cff-common-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
- #include "hb-subset-cff-common-private.hh"
++#include "hb-ot-cff-common.hh"
+#include "hb-ot-cff2-table.hh"
++#include "hb-subset-cff-common.hh"
+
+using namespace CFF;
+
+/**
+ * hb_plan_subset_cff_fdselect
+ * Determine an optimal FDSelect format according to a provided plan.
+ *
+ * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
+ * along with a font index remapping table
+ **/
+
+bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+ unsigned int fdCount,
+ const FDSelect &src, /* IN */
+ unsigned int &subset_fd_count /* OUT */,
+ unsigned int &subst_fdselect_size /* OUT */,
+ unsigned int &subst_fdselect_format /* OUT */,
+ hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+ FDMap &fdmap /* OUT */)
+{
+ subset_fd_count = 0;
+ subst_fdselect_size = 0;
+ subst_fdselect_format = 0;
+ unsigned int num_ranges = 0;
+
+ unsigned int subset_num_glyphs = glyphs.len;
+ if (subset_num_glyphs == 0)
+ return true;
+
+ {
+ /* use hb_set to determine the subset of font dicts */
+ hb_set_t *set = hb_set_create ();
+ if (set == &Null (hb_set_t))
+ return false;
+ hb_codepoint_t prev_fd = HB_SET_VALUE_INVALID;
+ for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+ {
+ hb_codepoint_t fd = src.get_fd (glyphs[i]);
+ set->add (fd);
+
+ if (fd != prev_fd)
+ {
+ num_ranges++;
+ prev_fd = fd;
+ subst_first_glyphs.push (i);
+ }
+ }
+
+ subset_fd_count = set->get_population ();
+ if (subset_fd_count == fdCount)
+ {
+ /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
+ hb_set_destroy (set);
+ return true;
+ }
+
+ /* create a fdmap */
+ fdmap.resize (fdCount);
+ for (unsigned int i = 0; i < fdmap.len; i++)
+ fdmap[i] = HB_SET_VALUE_INVALID;
+ hb_codepoint_t fd = HB_SET_VALUE_INVALID;
+ unsigned int fdindex = 0;
+ while (set->next (&fd))
+ fdmap[fd] = fdindex++;
+ assert (fdindex == subset_fd_count);
+ hb_set_destroy (set);
+ }
+
+ /* determine which FDSelect format is most compact */
+ if (subset_fd_count > 0xFF)
+ {
+ assert (src.format == 4);
+ subst_fdselect_format = 4;
+ subst_fdselect_size = FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges;
+ }
+ else
+ {
+ unsigned int format0_size = FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
+ unsigned int format3_size = FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges;
+
+ if (format0_size <= format3_size)
+ {
+ // subst_fdselect_format = 0;
+ subst_fdselect_size = format0_size;
+ subst_first_glyphs.fini ();
+ }
+ else
+ {
+ subst_fdselect_format = 3;
+ subst_fdselect_size = format3_size;
+ }
+ }
+
+ return true;
+}
+
+template <typename FDSELECT3_4>
+static inline bool
+serialize_fdselect_3_4 (hb_serialize_context_t *c,
+ unsigned int num_glyphs,
+ const FDSelect &src,
+ unsigned int size,
+ const hb_vector_t<hb_codepoint_t> &first_glyphs,
+ const FDMap &fdmap)
+{
+ TRACE_SERIALIZE (this);
+ FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
+ if (unlikely (p == nullptr)) return_trace (false);
+ p->nRanges.set (first_glyphs.len);
+ for (unsigned int i = 0; i < first_glyphs.len; i++)
+ {
+ hb_codepoint_t glyph = first_glyphs[i];
+ p->ranges[i].first.set (glyph);
+ p->ranges[i].fd.set (fdmap[src.get_fd (glyph)]);
+ }
+ p->sentinel().set (num_glyphs);
+ return_trace (true);
+}
+
+/**
+ * hb_serialize_cff_fdselect
+ * Serialize a subset FDSelect format planned above.
+ **/
+bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+ const hb_vector_t<hb_codepoint_t> &glyphs,
+ const FDSelect &src,
+ unsigned int fd_count,
+ unsigned int fdselect_format,
+ unsigned int size,
+ const hb_vector_t<hb_codepoint_t> &first_glyphs,
+ const FDMap &fdmap)
+{
+ TRACE_SERIALIZE (this);
+ FDSelect *p = c->allocate_min<FDSelect> ();
+ if (unlikely (p == nullptr)) return_trace (false);
+ p->format.set (fdselect_format);
+ size -= FDSelect::min_size;
+
+ switch (fdselect_format)
+ {
+ case 0:
+ {
+ FDSelect0 *p = c->allocate_size<FDSelect0> (size);
+ if (unlikely (p == nullptr)) return_trace (false);
+ for (unsigned int i = 0; i < glyphs.len; i++)
+ p->fds[i].set (fdmap[src.get_fd (glyphs[i])]);
+ break;
+ }
+
+ case 3:
+ return serialize_fdselect_3_4<FDSelect3> (c,
+ glyphs.len,
+ src,
+ size,
+ first_glyphs,
+ fdmap);
+
+ case 4:
+ return serialize_fdselect_3_4<FDSelect4> (c,
+ glyphs.len,
+ src,
+ size,
+ first_glyphs,
+ fdmap);
+
+ default:
+ assert(false);
+ }
+
+ return_trace (true);
+}
--- /dev/null
- #include "hb-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF_COMMON_PRIVATE_HH
+#define HB_SUBSET_CFF_COMMON_PRIVATE_HH
+
- #include "hb-cff-interp-cs-common-private.hh"
++#include "hb.hh"
+
+#include "hb-subset-plan.hh"
++#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+/* Used for writing a temporary charstring */
+struct ByteStrBuff : hb_vector_t<char, 1>
+{
+ inline bool encode_byte (unsigned char b)
+ {
+ return (push ((const char)b) != &Crap(char));
+ }
+
+ inline bool encode_num (const Number& n)
+ {
+ if (n.in_int_range ())
+ {
+ int v = n.to_int ();
+ if ((-1131 <= v) && (v <= 1131))
+ {
+ if ((-107 <= v) && (v <= 107))
+ return encode_byte (v + 139);
+ else if (v > 0)
+ {
+ v -= 108;
+ return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF);
+ }
+ else
+ {
+ v = -v - 108;
+ return encode_byte ((v >> 8) + OpCode_TwoByteNegInt0) && encode_byte (v & 0xFF);
+ }
+ }
+ assert ((v & ~0xFFFF) == 0);
+ return encode_byte (OpCode_shortint) &&
+ encode_byte ((v >> 8) & 0xFF) &&
+ encode_byte (v & 0xFF);
+ }
+ else
+ {
+ int32_t v = n.to_fixed ();
+ return encode_byte (OpCode_fixedcs) &&
+ encode_byte ((v >> 24) & 0xFF) &&
+ encode_byte ((v >> 16) & 0xFF) &&
+ encode_byte ((v >> 8) & 0xFF) &&
+ encode_byte (v & 0xFF);
+ }
+ }
+
+ inline bool encode_op (OpCode op)
+ {
+ if (Is_OpCode_ESC (op))
+ return encode_byte (OpCode_escape) &&
+ encode_byte (Unmake_OpCode_ESC (op));
+ else
+ return encode_byte (op);
+ }
+};
+
+struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1>
+{
+ inline void fini (void)
+ {
+ for (unsigned int i = 0; i < len; i++)
+ hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini ();
+ hb_vector_t<ByteStrBuff, 1>::fini ();
+ }
+};
+
+
+template <typename ACCESSOR, typename ENV, typename OPSET>
+struct SubrFlattener
+{
+ inline SubrFlattener (const ACCESSOR &acc_, const hb_vector_t<hb_codepoint_t> &glyphs_)
+ : acc (acc_),
+ glyphs (glyphs_)
+ {}
+
+ inline bool flatten (ByteStrBuffArray &flat_charstrings)
+ {
+ if (!flat_charstrings.resize (glyphs.len))
+ return false;
+ for (unsigned int i = 0; i < glyphs.len; i++)
+ flat_charstrings[i].init ();
+ for (unsigned int i = 0; i < glyphs.len; i++)
+ {
+ hb_codepoint_t glyph = glyphs[i];
+ const ByteStr str = (*acc.charStrings)[glyph];
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ CSInterpreter<ENV, OPSET, ByteStrBuff> interp;
+ interp.env.init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+ if (unlikely (!interp.interpret (flat_charstrings[i])))
+ return false;
+ }
+ return true;
+ }
+
+ const ACCESSOR &acc;
+ const hb_vector_t<hb_codepoint_t> &glyphs;
+};
+
+struct SubrRefMaps
+{
+ inline void init (void)
+ {
+ valid = false;
+ global_map = nullptr;
+ local_maps.init ();
+ }
+
+ inline void init (unsigned int fd_count)
+ {
+ valid = true;
+ global_map = hb_set_create ();
+ if (global_map == hb_set_get_empty ())
+ valid = false;
+ if (!local_maps.resize (fd_count))
+ valid = false;
+
+ for (unsigned int i = 0; i < local_maps.len; i++)
+ {
+ local_maps[i] = hb_set_create ();
+ if (local_maps[i] == hb_set_get_empty ())
+ valid = false;
+ }
+ }
+
+ inline void fini (void)
+ {
+ hb_set_destroy (global_map);
+ for (unsigned int i = 0; i < local_maps.len; i++)
+ hb_set_destroy (local_maps[i]);
+ local_maps.fini ();
+ }
+
+ bool is_valid (void) const { return valid; }
+ bool valid;
+ hb_set_t *global_map;
+ hb_vector_t<hb_set_t *> local_maps;
+};
+
+struct SubrRefMapPair
+{
+ inline void init (void) {}
+
+ hb_set_t *global_map;
+ hb_set_t *local_map;
+};
+
+template <typename ACCESSOR, typename ENV, typename OPSET>
+struct SubrSubsetter
+{
+ inline SubrSubsetter (const ACCESSOR &acc_, const hb_vector_t<hb_codepoint_t> &glyphs_)
+ : acc (acc_),
+ glyphs (glyphs_)
+ {}
+
+ bool collect_refs (SubrRefMaps& refmaps /*OUT*/)
+ {
+ refmaps.init (acc.fdCount);
+ if (unlikely (!refmaps.valid)) return false;
+ 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);
+ SubrRefMapPair pair = { refmaps.global_map, refmaps.local_maps[fd] };
+ CSInterpreter<ENV, OPSET, SubrRefMapPair> interp;
+ interp.env.init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+ if (unlikely (!interp.interpret (pair)))
+ return false;
+ }
+ return true;
+ }
+
+ const ACCESSOR &acc;
+ const hb_vector_t<hb_codepoint_t> &glyphs;
+};
+
+}; /* namespace CFF */
+
+HB_INTERNAL bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+ unsigned int fdCount,
+ const CFF::FDSelect &src, /* IN */
+ unsigned int &subset_fd_count /* OUT */,
+ unsigned int &subst_fdselect_size /* OUT */,
+ unsigned int &subst_fdselect_format /* OUT */,
+ hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+ CFF::FDMap &fdmap /* OUT */);
+
+HB_INTERNAL bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+ const hb_vector_t<hb_codepoint_t> &glyphs,
+ const CFF::FDSelect &src,
+ unsigned int fd_count,
+ unsigned int fdselect_format,
+ unsigned int size,
+ const hb_vector_t<hb_codepoint_t> &first_glyphs,
+ const CFF::FDMap &fdmap);
+
+#endif /* HB_SUBSET_CFF_COMMON_PRIVATE_HH */
--- /dev/null
- #include "hb-open-type-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
- #include "hb-subset-cff-common-private.hh"
++#include "hb-open-type.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff1.hh"
+#include "hb-subset-plan.hh"
++#include "hb-subset-cff-common.hh"
+#include "hb-cff1-interp-cs.hh"
+
+using namespace CFF;
+
+struct CFF1SubTableOffsets {
+ inline CFF1SubTableOffsets (void)
+ {
+ memset (this, 0, sizeof(*this));
+ localSubrsInfos.init ();
+ }
+
+ inline ~CFF1SubTableOffsets (void)
+ {
+ localSubrsInfos.fini ();
+ }
+
+ unsigned int nameIndexOffset;
+ TableInfo topDictInfo;
+ unsigned int stringIndexOffset;
+ TableInfo globalSubrsInfo;
+ unsigned int encodingOffset;
+ unsigned int charsetOffset;
+ TableInfo FDSelectInfo;
+ TableInfo FDArrayInfo;
+ TableInfo charStringsInfo;
+ TableInfo privateDictInfo;
+ hb_vector_t<TableInfo> localSubrsInfos;
+};
+
+struct CFF1TopDict_OpSerializer : OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const CFF1SubTableOffsets &offsets) const
+ {
+ TRACE_SERIALIZE (this);
+
+ switch (opstr.op)
+ {
+ case OpCode_charset:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charsetOffset));
+
+ case OpCode_Encoding:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.encodingOffset));
+
+ case OpCode_CharStrings:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
+
+ case OpCode_FDArray:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
+
+ case OpCode_FDSelect:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
+
+ case OpCode_Private:
+ {
+ if (unlikely (!UnsizedByteStr::serialize_int2 (c, offsets.privateDictInfo.size)))
+ return_trace (false);
+ if (unlikely (!UnsizedByteStr::serialize_int4 (c, offsets.privateDictInfo.offset)))
+ return_trace (false);
+ HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+ if (unlikely (p == nullptr)) return_trace (false);
+ p->set (OpCode_Private);
+ }
+ break;
+
+ default:
+ return_trace (copy_opstr (c, opstr));
+ }
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ switch (opstr.op)
+ {
+ case OpCode_charset:
+ case OpCode_Encoding:
+ case OpCode_CharStrings:
+ case OpCode_FDArray:
+ case OpCode_FDSelect:
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+ case OpCode_Private:
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+
+ default:
+ return opstr.str.len;
+ }
+ }
+};
+
+struct CFF1FontDict_OpSerializer : OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const TableInfo &privateDictInfo) const
+ {
+ TRACE_SERIALIZE (this);
+
+ if (opstr.op == OpCode_Private)
+ {
+ /* serialize the private dict size as a 2-byte integer */
+ if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size)))
+ return_trace (false);
+
+ /* serialize the private dict offset as a 4-byte integer */
+ if (unlikely (!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
+ return_trace (false);
+
+ /* serialize the opcode */
+ HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+ if (unlikely (p == nullptr)) return_trace (false);
+ p->set (OpCode_Private);
+
+ return_trace (true);
+ }
+ else
+ {
+ HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+ if (unlikely (d == nullptr)) return_trace (false);
+ memcpy (d, &opstr.str.str[0], opstr.str.len);
+ }
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ if (opstr.op == OpCode_Private)
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+ else
+ return opstr.str.len;
+ }
+};
+
+struct CFF1PrivateDict_OpSerializer : OpSerializer
+{
+ inline CFF1PrivateDict_OpSerializer (bool drop_hints_=false, bool flatten_subrs_=false)
+ : drop_hints (drop_hints_), flatten_subrs (flatten_subrs_) {}
+
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const unsigned int subrsOffset) const
+ {
+ TRACE_SERIALIZE (this);
+
+ if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+ return true;
+ if (opstr.op == OpCode_Subrs)
+ {
+ if (flatten_subrs)
+ return_trace (true);
+ else
+ return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
+ }
+ else
+ return_trace (copy_opstr (c, opstr));
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+ return 0;
+ if (opstr.op == OpCode_Subrs)
+ {
+ if (flatten_subrs)
+ return 0;
+ else
+ return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+ }
+ else
+ return opstr.str.len;
+ }
+
+ protected:
+ const bool drop_hints;
+ const bool flatten_subrs;
+};
+
+struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, ByteStrBuff>
+{
+ static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
+ {
+ if (unlikely (!SUPER::process_op (op, env, flatStr)))
+ return false;
+ switch (op)
+ {
+ case OpCode_hintmask:
+ case OpCode_cntrmask:
+ if (unlikely (!flatStr.encode_op (op)))
+ return false;
+ for (int i = -env.hintmask_size; i < 0; i++)
+ if (unlikely (!flatStr.encode_byte (env.substr[i])))
+ return false;
+ break;
+ default:
+ if (!SUPER::is_subr_op (op) &&
+ !SUPER::is_arg_op (op))
+ return flatStr.encode_op (op);
+ }
+ return true;
+ }
+
+ static inline void flush_stack (CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
+ {
+ for (unsigned int i = 0; i < env.argStack.size; i++)
+ flatStr.encode_num (env.argStack.elements[i]);
+ SUPER::flush_stack (env, flatStr);
+ }
+
+ private:
+ typedef CFF1CSOpSet<CFF1CSOpSet_Flatten, ByteStrBuff> SUPER;
+};
+
+struct CFF1CSOpSet_SubsetSubrs : CFF1CSOpSet<CFF1CSOpSet_SubsetSubrs, SubrRefMapPair>
+{
+ static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, SubrRefMapPair& refMapPair)
+ {
+ unsigned int subr_num;
+ switch (op) {
+ case OpCode_callsubr:
+ if (!unlikely (env.popSubrNum(env.localSubrs, subr_num)))
+ return false;
+ env.argStack.unpop ();
+ refMapPair.local_map->add (subr_num);
+ break;
+ case OpCode_callgsubr:
+ if (!unlikely (env.popSubrNum(env.globalSubrs, subr_num)))
+ return false;
+ env.argStack.unpop ();
+ refMapPair.global_map->add (subr_num);
+ break;
+ default:
+ break;
+ }
+ return CFF1CSOpSet<CFF1CSOpSet_SubsetSubrs, SubrRefMapPair>::process_op (op, env, refMapPair);
+ }
+};
+
+struct cff_subset_plan {
+ inline cff_subset_plan (void)
+ : final_size (0),
+ orig_fdcount (0),
+ subst_fdcount (1),
+ subst_fdselect_format (0),
+ offsets (),
+ flatten_subrs (true),
+ drop_hints (false)
+ {
+ topdict_sizes.init ();
+ topdict_sizes.resize (1);
+ subst_fdselect_first_glyphs.init ();
+ fdmap.init ();
+ subset_charstrings.init ();
+ flat_charstrings.init ();
+ privateDictInfos.init ();
+ subrRefMaps.init ();
+ }
+
+ inline ~cff_subset_plan (void)
+ {
+ topdict_sizes.fini ();
+ subst_fdselect_first_glyphs.fini ();
+ fdmap.fini ();
+ subset_charstrings.fini ();
+ flat_charstrings.fini ();
+ privateDictInfos.fini ();
+ subrRefMaps.fini ();
+ }
+
+ inline bool create (const OT::cff1::accelerator_subset_t &acc,
+ hb_subset_plan_t *plan)
+ {
+ final_size = 0;
+ orig_fdcount = acc.fdCount;
+ drop_hints = plan->drop_hints;
+
+ /* CFF header */
+ final_size += OT::cff1::static_size;
+
+ /* Name INDEX */
+ offsets.nameIndexOffset = final_size;
+ final_size += acc.nameIndex->get_size ();
+
+ /* top dict INDEX */
+ {
+ offsets.topDictInfo.offset = final_size;
+ CFF1TopDict_OpSerializer topSzr;
+ unsigned int topDictSize = TopDict::calculate_serialized_size (acc.topDicts[0], topSzr);
+ offsets.topDictInfo.offSize = calcOffSize(topDictSize);
+ final_size += CFF1IndexOf<TopDict>::calculate_serialized_size<CFF1TopDictValues> (offsets.topDictInfo.offSize, acc.topDicts, topdict_sizes, topSzr);
+ }
+
+ /* String INDEX */
+ offsets.stringIndexOffset = final_size;
+ final_size += acc.stringIndex->get_size ();
+
+ if (flatten_subrs)
+ {
+ /* Flatten global & local subrs */
+ SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten> flattener(acc, plan->glyphs);
+ if (!flattener.flatten (flat_charstrings))
+ return false;
+
+ /* no global/local subroutines */
+ offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */
+ }
+ else
+ {
+ /* Subset global & local subrs */
+ SubrSubsetter<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubsetSubrs> subsetter(acc, plan->glyphs);
+ if (!subsetter.collect_refs (subrRefMaps))
+ return false;
+
+ offsets.globalSubrsInfo.size = acc.globalSubrs->calculate_serialized_size (offsets.globalSubrsInfo.offSize, subrRefMaps.global_map, 1);
+ if (!offsets.localSubrsInfos.resize (orig_fdcount))
+ return false;
+ for (unsigned int i = 0; i < orig_fdcount; i++)
+ offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i], 1);
+ }
+ /* global subrs */
+ offsets.globalSubrsInfo.offset = final_size;
+ final_size += offsets.globalSubrsInfo.size;
+
+ /* Encoding */
+ offsets.encodingOffset = final_size;
+ if (acc.encoding != &Null(Encoding))
+ final_size += acc.encoding->get_size ();
+
+ /* Charset */
+ offsets.charsetOffset = final_size;
+ if (acc.charset != &Null(Charset))
+ final_size += acc.charset->get_size (acc.num_glyphs);
+
+ /* FDSelect */
+ if (acc.fdSelect != &Null(CFF1FDSelect))
+ {
+ offsets.FDSelectInfo.offset = final_size;
+ if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+ orig_fdcount,
+ *acc.fdSelect,
+ subst_fdcount,
+ offsets.FDSelectInfo.size,
+ subst_fdselect_format,
+ subst_fdselect_first_glyphs,
+ fdmap)))
+ return false;
+
+ if (!is_fds_subsetted ())
+ offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+ final_size += offsets.FDSelectInfo.size;
+ }
+
+ /* FDArray (FDIndex) */
+ if (acc.fdArray != &Null(CFF1FDArray)) {
+ offsets.FDArrayInfo.offset = final_size;
+ CFF1FontDict_OpSerializer fontSzr;
+ final_size += CFF1FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
+ }
+
+ /* CharStrings */
+ {
+ offsets.charStringsInfo.offset = final_size;
+ unsigned int dataSize = 0;
+ for (unsigned int i = 0; i < plan->glyphs.len; i++)
+ {
+ if (flatten_subrs)
+ {
+ ByteStrBuff &flatstr = flat_charstrings[i];
+ ByteStr str (&flatstr[0], flatstr.len);
+ subset_charstrings.push (str);
+ dataSize += flatstr.len;
+ }
+ else
+ {
+ const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
+ subset_charstrings.push (str);
+ dataSize += str.len;
+ }
+ }
+ offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+ final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+ }
+
+ /* private dicts & local subrs */
+ offsets.privateDictInfo.offset = final_size;
+ for (unsigned int i = 0; i < orig_fdcount; i++)
+ {
+ if (!fdmap.excludes (i))
+ {
+ unsigned int priv_size;
+ CFF1PrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs);
+ priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
+ TableInfo privInfo = { final_size, priv_size, 0 };
+ privateDictInfos.push (privInfo);
+ final_size += privInfo.size;
+ if (!flatten_subrs)
+ final_size += offsets.localSubrsInfos[i].size;
+ }
+ }
+
+ if (!acc.is_CID ())
+ offsets.privateDictInfo = privateDictInfos[0];
+
+ return ((subset_charstrings.len == plan->glyphs.len) &&
+ (privateDictInfos.len == subst_fdcount));
+ }
+
+ inline unsigned int get_final_size (void) const { return final_size; }
+
+ unsigned int final_size;
+ hb_vector_t<unsigned int> topdict_sizes;
+ CFF1SubTableOffsets offsets;
+
+ unsigned int orig_fdcount;
+ unsigned int subst_fdcount;
+ inline bool is_fds_subsetted (void) const { return subst_fdcount < orig_fdcount; }
+ unsigned int subst_fdselect_format;
+ hb_vector_t<hb_codepoint_t> subst_fdselect_first_glyphs;
+
+ /* font dict index remap table from fullset FDArray to subset FDArray.
+ * set to HB_SET_VALUE_INVALID if excluded from subset */
+ FDMap fdmap;
+
+ hb_vector_t<ByteStr> subset_charstrings;
+ ByteStrBuffArray flat_charstrings;
+ hb_vector_t<TableInfo> privateDictInfos;
+
+ SubrRefMaps subrRefMaps;
+
+ bool flatten_subrs;
+ bool drop_hints;
+};
+
+static inline bool _write_cff1 (const cff_subset_plan &plan,
+ const OT::cff1::accelerator_subset_t &acc,
+ const hb_vector_t<hb_codepoint_t>& glyphs,
+ unsigned int dest_sz,
+ void *dest)
+{
+ hb_serialize_context_t c (dest, dest_sz);
+
+ char RETURN_OP[1] = { OpCode_return };
+ const ByteStr NULL_SUBR (RETURN_OP, 1);
+
+ OT::cff1 *cff = c.start_serialize<OT::cff1> ();
+ if (unlikely (!c.extend_min (*cff)))
+ return false;
+
+ /* header */
+ cff->version.major.set (0x01);
+ cff->version.minor.set (0x00);
+ cff->nameIndex.set (cff->min_size);
+ cff->offSize.set (4); /* unused? */
+
+ /* name INDEX */
+ {
+ assert (cff->nameIndex == c.head - c.start);
+ CFF1NameIndex *dest = c.start_embed<CFF1NameIndex> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.nameIndex)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX");
+ return false;
+ }
+ }
+
+ /* top dict INDEX */
+ {
+ assert (plan.offsets.topDictInfo.offset == c.head - c.start);
+ CFF1IndexOf<TopDict> *dest = c.start_embed< CFF1IndexOf<TopDict> > ();
+ if (dest == nullptr) return false;
+ CFF1TopDict_OpSerializer topSzr;
+ if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize, acc.topDicts, plan.topdict_sizes, topSzr, plan.offsets)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict");
+ return false;
+ }
+ }
+
+ /* String INDEX */
+ {
+ assert (plan.offsets.stringIndexOffset == c.head - c.start);
+ CFF1StringIndex *dest = c.start_embed<CFF1StringIndex> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.stringIndex)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX");
+ return false;
+ }
+ }
+
+ /* global subrs */
+ {
+ assert (plan.offsets.globalSubrsInfo.offset != 0);
+ assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
+ CFF1Subrs *dest = c.start_embed<CFF1Subrs> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map, NULL_SUBR)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF global subrs");
+ return false;
+ }
+ }
+
+ /* Encoding */
+ if (acc.encoding != &Null(Encoding)){
+ assert (plan.offsets.encodingOffset == c.head - c.start);
+ Encoding *dest = c.start_embed<Encoding> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.encoding, acc.num_glyphs))) // XXX: TODO
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding");
+ return false;
+ }
+ }
+
+ /* Charset */
+ if (acc.charset != &Null(Charset))
+ {
+ assert (plan.offsets.charsetOffset == c.head - c.start);
+ Charset *dest = c.start_embed<Charset> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.charset, acc.num_glyphs))) // XXX: TODO
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset");
+ return false;
+ }
+ }
+
+ /* FDSelect */
+ if (acc.fdSelect != &Null(CFF1FDSelect))
+ {
+ assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+
+ if (plan.is_fds_subsetted ())
+ {
+ if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *acc.fdSelect, acc.fdCount,
+ plan.subst_fdselect_format, plan.offsets.FDSelectInfo.size,
+ plan.subst_fdselect_first_glyphs,
+ plan.fdmap)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect");
+ return false;
+ }
+ }
+ else
+ {
+ CFF1FDSelect *dest = c.start_embed<CFF1FDSelect> ();
+ if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDSelect");
+ return false;
+ }
+ }
+ }
+
+ /* FDArray (FD Index) */
+ if (acc.fdArray != &Null(CFF1FDArray))
+ {
+ assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+ CFF1FDArray *fda = c.start_embed<CFF1FDArray> ();
+ if (unlikely (fda == nullptr)) return false;
+ CFF1FontDict_OpSerializer fontSzr;
+ if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+ acc.fontDicts, plan.subst_fdcount, plan.fdmap,
+ fontSzr, plan.privateDictInfos)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray");
+ return false;
+ }
+ }
+
+ /* CharStrings */
+ {
+ assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+ CFF1CharStrings *cs = c.start_embed<CFF1CharStrings> ();
+ if (unlikely (cs == nullptr)) return false;
+ if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings");
+ return false;
+ }
+ }
+
+ /* private dicts & local subrs */
+ assert (plan.offsets.privateDictInfo.offset == c.head - c.start);
+ for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+ {
+ if (!plan.fdmap.excludes (i))
+ {
+ PrivateDict *pd = c.start_embed<PrivateDict> ();
+ if (unlikely (pd == nullptr)) return false;
+ unsigned int priv_size = plan.flatten_subrs? 0: plan.privateDictInfos[plan.fdmap[i]].size;
+ bool result;
+ CFF1PrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs);
+ /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
+ result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
+ if (unlikely (!result))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
+ return false;
+ }
+ if (!plan.flatten_subrs && (acc.privateDicts[i].subrsOffset != 0))
+ {
+ CFF1Subrs *subrs = c.start_embed<CFF1Subrs> ();
+ if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF1Subrs))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "CFF subset: local subrs unexpectedly null [%d]", i);
+ return false;
+ }
+ if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs, plan.offsets.localSubrsInfos[i].offSize, plan.subrRefMaps.local_maps[i], NULL_SUBR)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF local subrs [%d]", i);
+ return false;
+ }
+ }
+ }
+ }
+
+ assert (c.head == c.end);
+ c.end_serialize ();
+
+ return true;
+}
+
+static bool
+_hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc,
+ const char *data,
+ hb_subset_plan_t *plan,
+ hb_blob_t **prime /* OUT */)
+{
+ cff_subset_plan cff_plan;
+
+ if (unlikely (!cff_plan.create (acc, plan)))
+ {
+ DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
+ return false;
+ }
+
+ unsigned int cff_prime_size = cff_plan.get_final_size ();
+ char *cff_prime_data = (char *) calloc (1, cff_prime_size);
+
+ if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs,
+ cff_prime_size, cff_prime_data))) {
+ DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
+ free (cff_prime_data);
+ return false;
+ }
+
+ *prime = hb_blob_create (cff_prime_data,
+ cff_prime_size,
+ HB_MEMORY_MODE_READONLY,
+ cff_prime_data,
+ free);
+ return true;
+}
+
+/**
+ * hb_subset_cff1:
+ * Subsets the CFF table according to a provided plan.
+ *
+ * Return value: subsetted cff table.
+ **/
+bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+ hb_blob_t **prime /* OUT */)
+{
+ hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table<CFF::cff1> (plan->source);
+ const char *data = hb_blob_get_data(cff_blob, nullptr);
+
+ OT::cff1::accelerator_subset_t acc;
+ acc.init(plan->source);
+ bool result = likely (acc.is_valid ()) &&
+ _hb_subset_cff1 (acc, data, plan, prime);
+ hb_blob_destroy (cff_blob);
+ acc.fini ();
+
+ return result;
+}
--- /dev/null
- #include "hb-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF1_HH
+#define HB_SUBSET_CFF1_HH
+
++#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+ hb_blob_t **cff_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF1_HH */
--- /dev/null
- #include "hb-open-type-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
- #include "hb-subset-cff-common-private.hh"
++#include "hb-open-type.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff2.hh"
+#include "hb-subset-plan.hh"
++#include "hb-subset-cff-common.hh"
+#include "hb-cff2-interp-cs.hh"
+
+using namespace CFF;
+
+struct CFF2SubTableOffsets {
+ inline CFF2SubTableOffsets (void)
+ {
+ memset (this, 0, sizeof(*this));
+ localSubrsInfos.init ();
+ }
+
+ inline ~CFF2SubTableOffsets (void)
+ {
+ localSubrsInfos.fini ();
+ }
+
+ unsigned int topDictSize;
+ unsigned int varStoreOffset;
+ TableInfo FDSelectInfo;
+ TableInfo FDArrayInfo;
+ TableInfo charStringsInfo;
+ unsigned int privateDictsOffset;
+ TableInfo globalSubrsInfo;
+ hb_vector_t<TableInfo> localSubrsInfos;
+};
+
+struct CFF2TopDict_OpSerializer : OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const CFF2SubTableOffsets &offsets) const
+ {
+ TRACE_SERIALIZE (this);
+
+ switch (opstr.op)
+ {
+ case OpCode_vstore:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
+
+ case OpCode_CharStrings:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
+
+ case OpCode_FDArray:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
+
+ case OpCode_FDSelect:
+ return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
+
+ default:
+ return_trace (copy_opstr (c, opstr));
+ }
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ switch (opstr.op)
+ {
+ case OpCode_vstore:
+ case OpCode_CharStrings:
+ case OpCode_FDArray:
+ case OpCode_FDSelect:
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+ default:
+ return opstr.str.len;
+ }
+ }
+};
+
+struct CFF2FontDict_OpSerializer : OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const TableInfo& privDictInfo) const
+ {
+ TRACE_SERIALIZE (this);
+
+ if (opstr.op == OpCode_Private)
+ {
+ /* serialize the private dict size as a 2-byte integer */
+ if (unlikely (!UnsizedByteStr::serialize_int2 (c, privDictInfo.size)))
+ return_trace (false);
+
+ /* serialize the private dict offset as a 4-byte integer */
+ if (unlikely (!UnsizedByteStr::serialize_int4 (c, privDictInfo.offset)))
+ return_trace (false);
+
+ /* serialize the opcode */
+ HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+ if (unlikely (p == nullptr)) return_trace (false);
+ p->set (OpCode_Private);
+
+ return_trace (true);
+ }
+ else
+ {
+ HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+ if (unlikely (d == nullptr)) return_trace (false);
+ memcpy (d, &opstr.str.str[0], opstr.str.len);
+ }
+ return_trace (true);
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ if (opstr.op == OpCode_Private)
+ return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+ else
+ return opstr.str.len;
+ }
+};
+
+struct CFF2PrivateDict_OpSerializer : OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const unsigned int subrsOffset) const
+ {
+ TRACE_SERIALIZE (this);
+
+ if (opstr.op == OpCode_Subrs)
+ return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
+ else
+ return_trace (copy_opstr (c, opstr));
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ if (opstr.op == OpCode_Subrs)
+ return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+ else
+ return opstr.str.len;
+ }
+};
+
+struct CFF2PrivateDict_OpSerializer_DropHints : CFF2PrivateDict_OpSerializer
+{
+ inline bool serialize (hb_serialize_context_t *c,
+ const OpStr &opstr,
+ const unsigned int subrsOffset) const
+ {
+ if (DictOpSet::is_hint_op (opstr.op))
+ return true;
+ else
+ return CFF2PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset);
+ }
+
+ inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+ {
+ if (DictOpSet::is_hint_op (opstr.op))
+ return 0;
+ else
+ return CFF2PrivateDict_OpSerializer::calculate_serialized_size (opstr);
+ }
+};
+
+struct CFF2CSOpSet_SubsetSubrs : CFF2CSOpSet<CFF2CSOpSet_SubsetSubrs, SubrRefMapPair>
+{
+ static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair)
+ {
+ unsigned int subr_num;
+ switch (op) {
+ case OpCode_callsubr:
+ if (!unlikely (env.popSubrNum(env.localSubrs, subr_num)))
+ return false;
+ env.argStack.unpop ();
+ refMapPair.local_map->add (subr_num);
+ break;
+ case OpCode_callgsubr:
+ if (!unlikely (env.popSubrNum(env.globalSubrs, subr_num)))
+ return false;
+ env.argStack.unpop ();
+ refMapPair.global_map->add (subr_num);
+ break;
+ default:
+ break;
+ }
+ return CFF2CSOpSet<CFF2CSOpSet_SubsetSubrs, SubrRefMapPair>::process_op (op, env, refMapPair);
+ }
+};
+
+struct cff2_subset_plan {
+ inline cff2_subset_plan (void)
+ : final_size (0),
+ orig_fdcount (0),
+ subst_fdcount(1),
+ subst_fdselect_format (0)
+ {
+ subst_fdselect_first_glyphs.init ();
+ fdmap.init ();
+ subset_charstrings.init ();
+ privateDictInfos.init ();
+ subrRefMaps.init ();
+ }
+
+ inline ~cff2_subset_plan (void)
+ {
+ subst_fdselect_first_glyphs.fini ();
+ fdmap.fini ();
+ subset_charstrings.fini ();
+ privateDictInfos.fini ();
+ subrRefMaps.fini ();
+ }
+
+ inline bool create (const OT::cff2::accelerator_subset_t &acc,
+ hb_subset_plan_t *plan)
+ {
+ final_size = 0;
+ orig_fdcount = acc.fdArray->count;
+
+ drop_hints = plan->drop_hints;
+
+ /* CFF2 header */
+ final_size += OT::cff2::static_size;
+
+ /* top dict */
+ {
+ CFF2TopDict_OpSerializer topSzr;
+ offsets.topDictSize = TopDict::calculate_serialized_size (acc.topDict, topSzr);
+ final_size += offsets.topDictSize;
+ }
+
+ /* Subset global & local subrs */
+ {
+ SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubsetSubrs> subsetter(acc, plan->glyphs);
+ if (!subsetter.collect_refs (subrRefMaps))
+ return false;
+
+ offsets.globalSubrsInfo.size = acc.globalSubrs->calculate_serialized_size (offsets.globalSubrsInfo.offSize, subrRefMaps.global_map);
+ if (!offsets.localSubrsInfos.resize (orig_fdcount))
+ return false;
+ for (unsigned int i = 0; i < orig_fdcount; i++)
+ offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i]);
+ }
+
+ /* global subrs */
+ offsets.globalSubrsInfo.offset = final_size;
+ final_size += offsets.globalSubrsInfo.size;
+
+ /* variation store */
+ if (acc.varStore != &Null(CFF2VariationStore))
+ {
+ offsets.varStoreOffset = final_size;
+ final_size += acc.varStore->get_size ();
+ }
+
+ /* FDSelect */
+ if (acc.fdSelect != &Null(CFF2FDSelect))
+ {
+ offsets.FDSelectInfo.offset = final_size;
+ if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+ orig_fdcount,
+ *(const FDSelect *)acc.fdSelect,
+ subst_fdcount,
+ offsets.FDSelectInfo.size,
+ subst_fdselect_format,
+ subst_fdselect_first_glyphs,
+ fdmap)))
+ return false;
+
+ if (!is_fds_subsetted ())
+ offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+ final_size += offsets.FDSelectInfo.size;
+ }
+
+ /* FDArray (FDIndex) */
+ {
+ offsets.FDArrayInfo.offset = final_size;
+ CFF2FontDict_OpSerializer fontSzr;
+ final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
+ }
+
+ /* CharStrings */
+ {
+ offsets.charStringsInfo.offset = final_size;
+ unsigned int dataSize = 0;
+ for (unsigned int i = 0; i < plan->glyphs.len; i++)
+ {
+ const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
+ subset_charstrings.push (str);
+ dataSize += str.len;
+ }
+ offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+ final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+ }
+
+ /* private dicts & local subrs */
+ offsets.privateDictsOffset = final_size;
+ for (unsigned int i = 0; i < orig_fdcount; i++)
+ {
+ if (!fdmap.excludes (i))
+ {
+ unsigned int priv_size;
+ if (plan->drop_hints)
+ {
+ CFF2PrivateDict_OpSerializer_DropHints privSzr_drop;
+ priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr_drop);
+ }
+ else
+ {
+ CFF2PrivateDict_OpSerializer privSzr;
+ priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
+ }
+ TableInfo privInfo = { final_size, priv_size, 0 };
+ privateDictInfos.push (privInfo);
+ final_size += privInfo.size + offsets.localSubrsInfos[i].size;
+ }
+ }
+
+ return true;
+ }
+
+ inline unsigned int get_final_size (void) const { return final_size; }
+
+ unsigned int final_size;
+ CFF2SubTableOffsets offsets;
+
+ unsigned int orig_fdcount;
+ unsigned int subst_fdcount;
+ inline bool is_fds_subsetted (void) const { return subst_fdcount < orig_fdcount; }
+ unsigned int subst_fdselect_format;
+ hb_vector_t<hb_codepoint_t> subst_fdselect_first_glyphs;
+
+ FDMap fdmap;
+
+ hb_vector_t<ByteStr> subset_charstrings;
+ hb_vector_t<TableInfo> privateDictInfos;
+
+ SubrRefMaps subrRefMaps;
+
+ bool drop_hints;
+};
+
+static inline bool _write_cff2 (const cff2_subset_plan &plan,
+ const OT::cff2::accelerator_subset_t &acc,
+ const hb_vector_t<hb_codepoint_t>& glyphs,
+ unsigned int dest_sz,
+ void *dest)
+{
+ hb_serialize_context_t c (dest, dest_sz);
+
+ OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
+ if (unlikely (!c.extend_min (*cff2)))
+ return false;
+
+ /* header */
+ cff2->version.major.set (0x02);
+ cff2->version.minor.set (0x00);
+ cff2->topDict.set (OT::cff2::static_size);
+
+ /* top dict */
+ {
+ assert (cff2->topDict == c.head - c.start);
+ cff2->topDictSize.set (plan.offsets.topDictSize);
+ TopDict &dict = cff2 + cff2->topDict;
+ CFF2TopDict_OpSerializer topSzr;
+ if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
+ return false;
+ }
+ }
+
+ /* global subrs */
+ {
+ assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
+ CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
+ if (unlikely (dest == nullptr)) return false;
+ if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 global subrs");
+ return false;
+ }
+ }
+
+ /* variation store */
+ if (acc.varStore != &Null(CFF2VariationStore))
+ {
+ assert (plan.offsets.varStoreOffset == c.head - c.start);
+ CFF2VariationStore *dest = c.start_embed<CFF2VariationStore> ();
+ if (unlikely (!dest->serialize (&c, acc.varStore)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store");
+ return false;
+ }
+ }
+
+ /* FDSelect */
+ if (acc.fdSelect != &Null(CFF2FDSelect))
+ {
+ assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+
+ if (plan.is_fds_subsetted ())
+ {
+ if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
+ plan.subst_fdselect_format, plan.offsets.FDSelectInfo.size,
+ plan.subst_fdselect_first_glyphs,
+ plan.fdmap)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
+ return false;
+ }
+ }
+ else
+ {
+ CFF2FDSelect *dest = c.start_embed<CFF2FDSelect> ();
+ if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDSelect");
+ return false;
+ }
+ }
+ }
+
+ /* FDArray (FD Index) */
+ {
+ assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+ CFF2FDArray *fda = c.start_embed<CFF2FDArray> ();
+ if (unlikely (fda == nullptr)) return false;
+ CFF2FontDict_OpSerializer fontSzr;
+ if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+ acc.fontDicts, plan.subst_fdcount, plan.fdmap,
+ fontSzr, plan.privateDictInfos)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
+ return false;
+ }
+ }
+
+ /* CharStrings */
+ {
+ assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+ CFF2CharStrings *cs = c.start_embed<CFF2CharStrings> ();
+ if (unlikely (cs == nullptr)) return false;
+ if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
+ return false;
+ }
+ }
+
+ /* private dicts & local subrs */
+ assert (plan.offsets.privateDictsOffset == c.head - c.start);
+ for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+ {
+ if (!plan.fdmap.excludes (i))
+ {
+ PrivateDict *pd = c.start_embed<PrivateDict> ();
+ if (unlikely (pd == nullptr)) return false;
+ unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
+ bool result;
+ if (plan.drop_hints)
+ {
+ CFF2PrivateDict_OpSerializer_DropHints privSzr_drop;
+ result = pd->serialize (&c, acc.privateDicts[i], privSzr_drop, priv_size);
+ }
+ else
+ {
+ CFF2PrivateDict_OpSerializer privSzr;
+ result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
+ }
+ if (unlikely (!result))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i);
+ return false;
+ }
+ if (acc.privateDicts[i].subrsOffset != 0)
+ {
+ CFF2Subrs *subrs = c.start_embed<CFF2Subrs> ();
+ if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF2Subrs))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
+ return false;
+ }
+ if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs, plan.offsets.localSubrsInfos[i].offSize, plan.subrRefMaps.local_maps[i])))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 local subrs [%d]", i);
+ return false;
+ }
+ }
+ }
+ }
+
+ assert (c.head == c.end);
+ c.end_serialize ();
+
+ return true;
+}
+
+static bool
+_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
+ const char *data,
+ hb_subset_plan_t *plan,
+ hb_blob_t **prime /* OUT */)
+{
+ cff2_subset_plan cff2_plan;
+
+ if (unlikely (!cff2_plan.create (acc, plan)))
+ {
+ DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
+ return false;
+ }
+
+ unsigned int cff2_prime_size = cff2_plan.get_final_size ();
+ char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
+
+ if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
+ cff2_prime_size, cff2_prime_data))) {
+ DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
+ free (cff2_prime_data);
+ return false;
+ }
+
+ *prime = hb_blob_create (cff2_prime_data,
+ cff2_prime_size,
+ HB_MEMORY_MODE_READONLY,
+ cff2_prime_data,
+ free);
+ return true;
+}
+
+/**
+ * hb_subset_cff2:
+ * Subsets the CFF2 table according to a provided plan.
+ *
+ * Return value: subsetted cff2 table.
+ **/
+bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+ hb_blob_t **prime /* OUT */)
+{
+ hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
+ const char *data = hb_blob_get_data(cff2_blob, nullptr);
+
+ OT::cff2::accelerator_subset_t acc;
+ acc.init(plan->source);
+ bool result = likely (acc.is_valid ()) &&
+ _hb_subset_cff2 (acc, data, plan, prime);
+
+ hb_blob_destroy (cff2_blob);
+ acc.fini ();
+
+ return result;
+}
--- /dev/null
- #include "hb-private.hh"
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF2_HH
+#define HB_SUBSET_CFF2_HH
+
++#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+ hb_blob_t **cff2_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF2_HH */