/* * Copyright © 2018 Adobe Inc. * * 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 BlendArg : Number { inline void init (void) { Number::init (); deltas.init (); } inline void fini (void) { Number::fini (); deltas.fini_deep (); } inline void set_int (int v) { reset_blends (); Number::set_int (v); } inline void set_fixed (int32_t v) { reset_blends (); Number::set_fixed (v); } inline void set_real (double v) { reset_blends (); Number::set_real (v); } inline void set_blends (unsigned int numValues_, unsigned int valueIndex_, unsigned int numBlends, const hb_array_t &blends_) { numValues = numValues_; valueIndex = valueIndex_; deltas.resize (numBlends); for (unsigned int i = 0; i < numBlends; i++) deltas[i] = blends_[i]; } inline bool blending (void) const { return deltas.len > 0; } inline void reset_blends (void) { numValues = valueIndex = 0; deltas.resize (0); } unsigned int numValues; unsigned int valueIndex; hb_vector_t deltas; }; typedef InterpEnv BlendInterpEnv; typedef BiasedSubrs CFF2BiasedSubrs; struct CFF2CSInterpEnv : CSInterpEnv { template inline void init (const ByteStr &str, ACC &acc, unsigned int fd, const int *coords_=nullptr, unsigned int num_coords_=0) { SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); coords = coords_; num_coords = num_coords_; varStore = acc.varStore; seen_blend = false; seen_vsindex_ = false; scalars.init (); do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore)); set_ivs (acc.privateDicts[fd].ivs); } inline void fini (void) { scalars.fini (); SUPER::fini (); } inline OpCode fetch_op (void) { if (this->substr.avail ()) return SUPER::fetch_op (); /* make up return or endchar op */ if (this->callStack.is_empty ()) return OpCode_endchar; else return OpCode_return; } inline const BlendArg& eval_arg (unsigned int i) { BlendArg &arg = argStack[i]; blend_arg (arg); return arg; } inline const BlendArg& pop_arg (void) { BlendArg &arg = argStack.pop (); blend_arg (arg); return arg; } inline void process_blend (void) { if (!seen_blend) { region_count = varStore->varStore.get_region_index_count (get_ivs ()); if (do_blend) { scalars.resize (region_count); varStore->varStore.get_scalars (get_ivs (), (int *)coords, num_coords, &scalars[0], region_count); } seen_blend = true; } } inline void process_vsindex (void) { unsigned int index = argStack.pop_uint (); if (unlikely (seen_vsindex () || seen_blend)) { set_error (); } else { set_ivs (index); } seen_vsindex_ = true; } inline unsigned int get_region_count (void) const { return region_count; } inline void set_region_count (unsigned int region_count_) { region_count = region_count_; } inline unsigned int get_ivs (void) const { return ivs; } inline void set_ivs (unsigned int ivs_) { ivs = ivs_; } inline bool seen_vsindex (void) const { return seen_vsindex_; } protected: inline void blend_arg (BlendArg &arg) { if (do_blend && arg.blending ()) { if (likely (scalars.len == arg.deltas.len)) { double v = arg.to_real (); for (unsigned int i = 0; i < scalars.len; i++) { v += (double)scalars[i] * arg.deltas[i].to_real (); } arg.set_real (v); arg.deltas.resize (0); } } } protected: const int *coords; unsigned int num_coords; const CFF2VariationStore *varStore; unsigned int region_count; unsigned int ivs; hb_vector_t scalars; bool do_blend; bool seen_vsindex_; bool seen_blend; typedef CSInterpEnv SUPER; }; template > struct CFF2CSOpSet : CSOpSet { static inline void process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param) { switch (op) { case OpCode_callsubr: case OpCode_callgsubr: /* a subroutine number shoudln't be a blended value */ if (unlikely (env.argStack.peek ().blending ())) { env.set_error (); break; } SUPER::process_op (op, env, param); break; case OpCode_blendcs: OPSET::process_blend (env, param); break; case OpCode_vsindexcs: if (unlikely (env.argStack.peek ().blending ())) { env.set_error (); break; } OPSET::process_vsindex (env, param); break; default: SUPER::process_op (op, env, param); } } static inline void process_blend (CFF2CSInterpEnv &env, PARAM& param) { unsigned int n, k; env.process_blend (); k = env.get_region_count (); n = env.argStack.pop_uint (); /* copy the blend values into blend array of the default values */ unsigned int start = env.argStack.get_count () - ((k+1) * n); /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ if (unlikely (start > env.argStack.get_count ())) { env.set_error (); return; } for (unsigned int i = 0; i < n; i++) { const hb_array_t blends = env.argStack.get_subarray (start + n + (i * k)); env.argStack[start + i].set_blends (n, i, k, blends); } /* pop off blend values leaving default values now adorned with blend values */ env.argStack.pop (k * n); } static inline void process_vsindex (CFF2CSInterpEnv &env, PARAM& param) { env.process_vsindex (); env.clear_args (); } private: typedef CSOpSet SUPER; }; template struct CFF2CSInterpreter : CSInterpreter {}; } /* namespace CFF */ #endif /* HB_CFF2_INTERP_CS_HH */