From b51418f596097aa5d1b28fb0a02b613c36bacd8e Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 8 Oct 2018 15:05:36 -0700 Subject: [PATCH] added CFF2 get_extents added source hb-ot-cff2-table.cc augmented VariationData to return scalars misc bug fixes, renaming, cleanup --- src/Makefile.sources | 2 + src/hb-cff-interp-common.hh | 37 +++++---- src/hb-cff-interp-cs-common.hh | 134 +++++++++++++++--------------- src/hb-cff-interp-dict-common.hh | 6 +- src/hb-cff2-interp-cs.hh | 102 +++++++++++++++++++---- src/hb-ot-cff1-table.hh | 4 +- src/hb-ot-cff2-table.cc | 135 +++++++++++++++++++++++++++++++ src/hb-ot-cff2-table.hh | 71 +++++++++------- src/hb-ot-face.cc | 1 + src/hb-ot-face.hh | 1 + src/hb-ot-font.cc | 4 + src/hb-ot-layout-common.hh | 28 ++++++- src/hb-subset-cff1.cc | 2 +- src/hb-subset-cff2.cc | 4 +- test/api/test-ot-extents-cff.c | 36 +++++++++ 15 files changed, 426 insertions(+), 141 deletions(-) create mode 100644 src/hb-ot-cff2-table.cc diff --git a/src/Makefile.sources b/src/Makefile.sources index 7107ff876..9c7c3cc85 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -30,6 +30,7 @@ HB_BASE_sources = \ hb-ot-cff1-table.hh \ hb-ot-cff1-table.cc \ hb-ot-cff2-table.hh \ + hb-ot-cff2-table.cc \ hb-ot-vorg-table.hh \ hb-ot-hdmx-table.hh \ hb-ot-head-table.hh \ @@ -227,6 +228,7 @@ HB_SUBSET_sources = \ hb-subset-cff2.cc \ hb-subset-cff-common.cc \ hb-ot-cff1-table.cc \ + hb-ot-cff2-table.cc \ hb-subset-input.cc \ hb-subset-input.hh \ hb-subset-plan.cc \ diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh index 0870f45df..266270cc7 100644 --- a/src/hb-cff-interp-common.hh +++ b/src/hb-cff-interp-common.hh @@ -298,19 +298,13 @@ struct Number inline const Number &operator += (const Number &n) { - switch (format) - { - default: - case NumInt: - u.int_val += n.to_int (); - break; - case NumFixed: - u.fixed_val += n.to_fixed (); - break; - case NumReal: - u.real_val += n.to_real (); - break; - } + if (format == NumReal || n.format == NumReal) + set_real (to_real () + n.to_real ()); + else if (format == NumFixed || n.format == NumFixed) + set_fixed (to_fixed () + n.to_fixed ()); + else + set_int (to_int () + n.to_int ()); + return *this; } @@ -453,8 +447,7 @@ struct Stack inline void fini (void) { - for (unsigned int i = 0; i < elements.len; i++) - elements[i].fini (); + elements.fini_deep (); } inline const ELEM& operator [] (unsigned int i) const @@ -477,12 +470,12 @@ struct Stack return Crap(ELEM); } - inline const ELEM& pop (void) + inline ELEM& pop (void) { if (likely (count > 0)) return elements[--count]; else - return Null(ELEM); + return Crap(ELEM); } inline void pop (unsigned int n) @@ -678,6 +671,16 @@ struct InterpEnv return true; } + inline const ARG& eval_arg (unsigned int i) + { + return argStack[i]; + } + + inline ARG& pop_arg (void) + { + return argStack.pop (); + } + inline void pop_n_args (unsigned int n) { assert (n <= argStack.get_count ()); diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh index 72f4061fb..c924f5186 100644 --- a/src/hb-cff-interp-cs-common.hh +++ b/src/hb-cff-interp-cs-common.hh @@ -154,19 +154,19 @@ struct CSInterpEnv : InterpEnv inline unsigned int move_x_with_arg (unsigned int i) { - pt.move_x (SUPER::argStack[i]); + pt.move_x (SUPER::eval_arg (i)); return i + 1; } inline unsigned int move_y_with_arg (unsigned int i) { - pt.move_y (SUPER::argStack[i]); + pt.move_y (SUPER::eval_arg (i)); return i + 1; } inline unsigned int move_xy_with_arg (unsigned int i) { - pt.move (SUPER::argStack[i], SUPER::argStack[i+1]); + pt.move (SUPER::eval_arg (i), SUPER::eval_arg (i+1)); return i + 2; } @@ -382,8 +382,8 @@ struct PathProcs static inline void rmoveto (ENV &env, PARAM& param) { Point pt1 = env.get_pt (); - const Number &dy = env.argStack.pop (); - const Number &dx = env.argStack.pop (); + const Number &dy = env.pop_arg (); + const Number &dx = env.pop_arg (); pt1.move (dx, dy); PATH::moveto (env, param, pt1); } @@ -391,14 +391,14 @@ struct PathProcs static inline void hmoveto (ENV &env, PARAM& param) { Point pt1 = env.get_pt (); - pt1.move_x (env.argStack.pop ()); + pt1.move_x (env.pop_arg ()); PATH::moveto (env, param, pt1); } static inline void vmoveto (ENV &env, PARAM& param) { Point pt1 = env.get_pt (); - pt1.move_y (env.argStack.pop ()); + pt1.move_y (env.pop_arg ()); PATH::moveto (env, param, pt1); } @@ -407,7 +407,7 @@ struct PathProcs for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); PATH::line (env, param, pt1); } } @@ -419,15 +419,15 @@ struct PathProcs for (; i + 2 <= env.argStack.get_count (); i += 2) { pt1 = env.get_pt (); - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); PATH::line (env, param, pt1); - pt1.move_y (env.argStack[i+1]); + pt1.move_y (env.eval_arg (i+1)); PATH::line (env, param, pt1); } if (i < env.argStack.get_count ()) { pt1 = env.get_pt (); - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); PATH::line (env, param, pt1); } } @@ -439,15 +439,15 @@ struct PathProcs for (; i + 2 <= env.argStack.get_count (); i += 2) { pt1 = env.get_pt (); - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); PATH::line (env, param, pt1); - pt1.move_x (env.argStack[i+1]); + pt1.move_x (env.eval_arg (i+1)); PATH::line (env, param, pt1); } if (i < env.argStack.get_count ()) { pt1 = env.get_pt (); - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); PATH::line (env, param, pt1); } } @@ -457,11 +457,11 @@ struct PathProcs for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); Point pt2 = pt1; - pt2.move (env.argStack[i+2], env.argStack[i+3]); + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); Point pt3 = pt2; - pt3.move (env.argStack[i+4], env.argStack[i+5]); + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); PATH::curve (env, param, pt1, pt2, pt3); } } @@ -472,17 +472,17 @@ struct PathProcs for (; i + 6 <= env.argStack.get_count (); i += 6) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); Point pt2 = pt1; - pt2.move (env.argStack[i+2], env.argStack[i+3]); + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); Point pt3 = pt2; - pt3.move (env.argStack[i+4], env.argStack[i+5]); + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); PATH::curve (env, param, pt1, pt2, pt3); } for (; i + 2 <= env.argStack.get_count (); i += 2) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); PATH::line (env, param, pt1); } } @@ -494,17 +494,17 @@ struct PathProcs for (; i + 2 <= line_limit; i += 2) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); PATH::line (env, param, pt1); } for (; i + 6 <= env.argStack.get_count (); i += 6) { Point pt1 = env.get_pt (); - pt1.move (env.argStack[i], env.argStack[i+1]); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); Point pt2 = pt1; - pt2.move (env.argStack[i+2], env.argStack[i+3]); + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); Point pt3 = pt2; - pt3.move (env.argStack[i+4], env.argStack[i+5]); + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); PATH::curve (env, param, pt1, pt2, pt3); } } @@ -514,14 +514,14 @@ struct PathProcs unsigned int i = 0; Point pt1 = env.get_pt (); if ((env.argStack.get_count () & 1) != 0) - pt1.move_x (env.argStack[i++]); + pt1.move_x (env.eval_arg (i++)); for (; i + 4 <= env.argStack.get_count (); i += 4) { - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); Point pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); Point pt3 = pt2; - pt3.move_y (env.argStack[i+3]); + pt3.move_y (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = env.get_pt (); } @@ -532,14 +532,14 @@ struct PathProcs unsigned int i = 0; Point pt1 = env.get_pt (); if ((env.argStack.get_count () & 1) != 0) - pt1.move_y (env.argStack[i++]); + pt1.move_y (env.eval_arg (i++)); for (; i + 4 <= env.argStack.get_count (); i += 4) { - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); Point pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); Point pt3 = pt2; - pt3.move_x (env.argStack[i+3]); + pt3.move_x (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = env.get_pt (); } @@ -552,33 +552,33 @@ struct PathProcs if ((env.argStack.get_count () % 8) >= 4) { Point pt1 = env.get_pt (); - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); Point pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); Point pt3 = pt2; - pt3.move_x (env.argStack[i+3]); + pt3.move_x (env.eval_arg (i+3)); i += 4; for (; i + 8 <= env.argStack.get_count (); i += 8) { PATH::curve (env, param, pt1, pt2, pt3); pt1 = env.get_pt (); - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); pt3 = pt2; - pt3.move_y (env.argStack[i+3]); + pt3.move_y (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = pt3; - pt1.move_y (env.argStack[i+4]); + pt1.move_y (env.eval_arg (i+4)); pt2 = pt1; - pt2.move (env.argStack[i+5], env.argStack[i+6]); + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); pt3 = pt2; - pt3.move_x (env.argStack[i+7]); + pt3.move_x (env.eval_arg (i+7)); } if (i < env.argStack.get_count ()) - pt3.move_y (env.argStack[i]); + pt3.move_y (env.eval_arg (i)); PATH::curve (env, param, pt1, pt2, pt3); } else @@ -586,21 +586,21 @@ struct PathProcs for (; i + 8 <= env.argStack.get_count (); i += 8) { pt1 = env.get_pt (); - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); pt3 = pt2; - pt3.move_x (env.argStack[i+3]); + pt3.move_x (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = pt3; - pt1.move_x (env.argStack[i+4]); + pt1.move_x (env.eval_arg (i+4)); pt2 = pt1; - pt2.move (env.argStack[i+5], env.argStack[i+6]); + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); pt3 = pt2; - pt3.move_y (env.argStack[i+7]); + pt3.move_y (env.eval_arg (i+7)); if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) - pt3.move_x (env.argStack[i+8]); + pt3.move_x (env.eval_arg (i+8)); PATH::curve (env, param, pt1, pt2, pt3); } } @@ -613,33 +613,33 @@ struct PathProcs if ((env.argStack.get_count () % 8) >= 4) { Point pt1 = env.get_pt (); - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); Point pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); Point pt3 = pt2; - pt3.move_y (env.argStack[i+3]); + pt3.move_y (env.eval_arg (i+3)); i += 4; for (; i + 8 <= env.argStack.get_count (); i += 8) { PATH::curve (env, param, pt1, pt2, pt3); pt1 = env.get_pt (); - pt1.move_y (env.argStack[i]); + pt1.move_y (env.eval_arg (i)); pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); pt3 = pt2; - pt3.move_x (env.argStack[i+3]); + pt3.move_x (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = pt3; - pt1.move_x (env.argStack[i+4]); + pt1.move_x (env.eval_arg (i+4)); pt2 = pt1; - pt2.move (env.argStack[i+5], env.argStack[i+6]); + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); pt3 = pt2; - pt3.move_y (env.argStack[i+7]); + pt3.move_y (env.eval_arg (i+7)); } if (i < env.argStack.get_count ()) - pt3.move_x (env.argStack[i]); + pt3.move_x (env.eval_arg (i)); PATH::curve (env, param, pt1, pt2, pt3); } else @@ -647,21 +647,21 @@ struct PathProcs for (; i + 8 <= env.argStack.get_count (); i += 8) { pt1 = env.get_pt (); - pt1.move_x (env.argStack[i]); + pt1.move_x (env.eval_arg (i)); pt2 = pt1; - pt2.move (env.argStack[i+1], env.argStack[i+2]); + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); pt3 = pt2; - pt3.move_y (env.argStack[i+3]); + pt3.move_y (env.eval_arg (i+3)); PATH::curve (env, param, pt1, pt2, pt3); pt1 = pt3; - pt1.move_y (env.argStack[i+4]); + pt1.move_y (env.eval_arg (i+4)); pt2 = pt1; - pt2.move (env.argStack[i+5], env.argStack[i+6]); + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); pt3 = pt2; - pt3.move_x (env.argStack[i+7]); + pt3.move_x (env.eval_arg (i+7)); if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) - pt3.move_y (env.argStack[i+8]); + pt3.move_y (env.eval_arg (i+8)); PATH::curve (env, param, pt1, pt2, pt3); } } diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh index 1c7d28f62..d1b28a5f5 100644 --- a/src/hb-cff-interp-dict-common.hh +++ b/src/hb-cff-interp-dict-common.hh @@ -64,11 +64,7 @@ struct DictValues inline void fini (void) { - for (unsigned int i = 0; i < values.len; i++) - { - values[i].fini (); - } - values.fini (); + values.fini_deep (); } inline void addOp (OpCode op, const SubByteStr& substr = SubByteStr ()) diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh index 2a68da8d9..07408b329 100644 --- a/src/hb-cff2-interp-cs.hh +++ b/src/hb-cff2-interp-cs.hh @@ -44,10 +44,7 @@ struct BlendArg : Number inline void fini (void) { Number::fini (); - - for (unsigned int i = 0; i < deltas.len; i++) - deltas[i].fini (); - deltas.fini (); + deltas.fini_deep (); } inline void set_int (int v) { reset_blends (); Number::set_int (v); } @@ -64,7 +61,7 @@ struct BlendArg : Number deltas[i] = blends_[i]; } - inline bool blended (void) const { return deltas.len > 0; } + inline bool blending (void) const { return deltas.len > 0; } inline void reset_blends (void) { numValues = valueIndex = 0; @@ -81,11 +78,26 @@ typedef InterpEnv BlendInterpEnv; struct CFF2CSInterpEnv : CSInterpEnv { template - inline void init (const ByteStr &str, ACC &acc, unsigned int fd) + inline void init (const ByteStr &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int coords_count_=0, + const CFF2VariationStore *varStore_=nullptr) { SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); - set_region_count (acc.region_count); - set_vsindex (acc.privateDicts[fd].vsindex); + + coords = coords_; + num_coords = coords_count_; + varStore = 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 bool fetch_op (OpCode &op) @@ -101,25 +113,80 @@ struct CFF2CSInterpEnv : CSInterpEnv return true; } + inline const BlendArg& eval_arg (unsigned int i) + { + return blend_arg (argStack[i]); + } + + inline const BlendArg& pop_arg (void) + { + return blend_arg (argStack.pop ()); + } + + 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; - if (likely (argStack.check_pop_uint (index))) - set_vsindex (argStack.check_pop_uint (index)); + if (do_blend) + { + unsigned int index; + if (likely (!seen_vsindex && !seen_blend && argStack.check_pop_uint (index))) + set_ivs (argStack.check_pop_uint (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_vsindex (void) const { return vsindex; } - inline void set_vsindex (unsigned int vsindex_) { vsindex = vsindex_; } + inline unsigned int get_ivs (void) const { return ivs; } + inline void set_ivs (unsigned int ivs_) { ivs = ivs_; } protected: + inline BlendArg& blend_arg (BlendArg &arg) + { + if (do_blend && arg.blending ()) + { + if (likely (scalars.len == arg.deltas.len)) + { + float v = arg.to_real (); + for (unsigned int i = 0; i < scalars.len; i++) + { + v += scalars[i] * arg.deltas[i].to_real (); + } + arg.set_real (v); + arg.deltas.resize (0); + } + } + return arg; + } + + protected: + const int *coords; + unsigned int num_coords; + const CFF2VariationStore *varStore; unsigned int region_count; - unsigned int vsindex; + unsigned int ivs; + hb_vector_t scalars; + bool do_blend; + bool seen_vsindex; + bool seen_blend; typedef CSInterpEnv SUPER; }; - template > struct CFF2CSOpSet : CSOpSet { @@ -129,7 +196,7 @@ struct CFF2CSOpSet : CSOpSet case OpCode_callsubr: case OpCode_callgsubr: /* a subroutine number shoudln't be a blended value */ - if (unlikely (env.argStack.peek ().blended ())) + if (unlikely (env.argStack.peek ().blending ())) return false; return SUPER::process_op (op, env, param); @@ -137,7 +204,7 @@ struct CFF2CSOpSet : CSOpSet return OPSET::process_blend (env, param); case OpCode_vsindexcs: - if (unlikely (env.argStack.peek ().blended ())) + if (unlikely (env.argStack.peek ().blending ())) return false; OPSET::process_vsindex (env, param); break; @@ -152,6 +219,7 @@ struct CFF2CSOpSet : CSOpSet { unsigned int n, k; + env.process_blend (); k = env.get_region_count (); if (unlikely (!env.argStack.check_pop_uint (n) || (k+1) * n > env.argStack.get_count ())) diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index 30d8c319b..67836d7ba 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -1093,9 +1093,7 @@ struct cff1 sc.end_processing (); topDict.fini (); fontDicts.fini (); - for (unsigned int i = 0; i < privateDicts.len; i++) - privateDicts[i].fini (); - privateDicts.fini (); + privateDicts.fini_deep (); hb_blob_destroy (blob); blob = nullptr; } diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc new file mode 100644 index 000000000..d76f6b752 --- /dev/null +++ b/src/hb-ot-cff2-table.cc @@ -0,0 +1,135 @@ +/* + * 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-ot-cff2-table.hh" +#include "hb-cff2-interp-cs.hh" + +using namespace CFF; + +struct ExtentsParam +{ + inline void init (void) + { + path_open = false; + min_x.set_int (0x7FFFFFFF); + min_y.set_int (0x7FFFFFFF); + max_x.set_int (-0x80000000); + max_y.set_int (-0x80000000); + } + + inline void start_path (void) { path_open = true; } + inline void end_path (void) { path_open = false; } + inline bool is_path_open (void) const { return path_open; } + + inline void update_bounds (const Point &pt) + { + if (pt.x < min_x) min_x = pt.x; + if (pt.x > max_x) max_x = pt.x; + if (pt.y < min_y) min_y = pt.y; + if (pt.y > max_y) max_y = pt.y; + } + + bool path_open; + Number min_x; + Number min_y; + Number max_x; + Number max_y; +}; + +struct CFF2PathProcs_Extents : PathProcs +{ + static inline void moveto (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt) + { + param.end_path (); + env.moveto (pt); + } + + static inline void line (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + env.moveto (pt1); + param.update_bounds (env.get_pt ()); + } + + static inline void curve (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + /* include control points */ + param.update_bounds (pt1); + param.update_bounds (pt2); + env.moveto (pt3); + param.update_bounds (env.get_pt ()); + } +}; + +struct CFF2CSOpSet_Extents : CFF2CSOpSet {}; + +bool OT::cff2::accelerator_t::get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + const int *coords, + unsigned int num_coords) const +{ + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + CFF2CSInterpreter interp; + const ByteStr str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, coords, num_coords, varStore); + ExtentsParam param; + param.init (); + if (unlikely (!interp.interpret (param))) return false; + + if (param.min_x >= param.max_x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = (int32_t)param.min_x.floor (); + extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing; + } + if (param.min_y >= param.max_y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = (int32_t)param.max_y.ceil (); + extents->height = (int32_t)param.min_y.floor () - extents->y_bearing; + } + + return true; +} diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh index 196c73ee8..2e0579329 100644 --- a/src/hb-ot-cff2-table.hh +++ b/src/hb-ot-cff2-table.hh @@ -265,7 +265,7 @@ struct CFF2PrivateDictValues_Base : DictValues DictValues::init (); subrsOffset = 0; localSubrs = &Null(CFF2Subrs); - vsindex = 0; + ivs = 0; } inline void fini (void) @@ -286,15 +286,42 @@ struct CFF2PrivateDictValues_Base : DictValues unsigned int subrsOffset; const CFF2Subrs *localSubrs; - unsigned int vsindex; + unsigned int ivs; }; typedef CFF2PrivateDictValues_Base CFF2PrivateDictValues_Subset; typedef CFF2PrivateDictValues_Base CFF2PrivateDictValues; +struct CFF2PrivDictInterpEnv : NumInterpEnv +{ + inline void init (const ByteStr &str) + { + NumInterpEnv::init (str); + ivs = 0; + seen_vsindex = false; + } + + inline void process_vsindex (void) + { + unsigned int index; + if (likely (!seen_vsindex && argStack.check_pop_uint (index))) + { + set_ivs (argStack.check_pop_uint (index)); + } + seen_vsindex = true; + } + + inline unsigned int get_ivs (void) const { return ivs; } + inline void set_ivs (unsigned int ivs_) { ivs = ivs_; } + + protected: + unsigned int ivs; + bool seen_vsindex; +}; + struct CFF2PrivateDictOpSet : DictOpSet { - static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2PrivateDictValues& dictval) + static inline bool process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues& dictval) { NumDictVal val; val.init (); @@ -327,8 +354,8 @@ struct CFF2PrivateDictOpSet : DictOpSet env.clear_args (); break; case OpCode_vsindexdict: - if (unlikely (!env.argStack.check_pop_uint (dictval.vsindex))) - return false; + env.process_vsindex (); + dictval.ivs = env.get_ivs (); break; case OpCode_blenddict: break; @@ -347,7 +374,7 @@ struct CFF2PrivateDictOpSet : DictOpSet struct CFF2PrivateDictOpSet_Subset : DictOpSet { - static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2PrivateDictValues_Subset& dictval) + static inline bool process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues_Subset& dictval) { switch (op) { case OpCode_BlueValues: @@ -455,11 +482,6 @@ struct cff2 if (num_glyphs != sc.get_num_glyphs ()) { fini (); return; } - if (varStore != &Null(CFF2VariationStore)) - region_count = varStore->varStore.get_region_count (); - else - region_count = 0; - fdCount = fdArray->count; privateDicts.resize (fdCount); @@ -476,7 +498,7 @@ struct cff2 const ByteStr privDictStr (StructAtOffsetOrNull (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } - DictInterpreter priv_interp; + DictInterpreter priv_interp; priv_interp.env.init(privDictStr); if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } @@ -491,25 +513,13 @@ struct cff2 { sc.end_processing (); fontDicts.fini (); - for (unsigned int i = 0; i < privateDicts.len; i++) - privateDicts[i].fini (); - privateDicts.fini (); + privateDicts.fini_deep (); 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; @@ -527,10 +537,16 @@ struct cff2 hb_vector_t privateDicts; unsigned int num_glyphs; - unsigned int region_count; }; - typedef accelerator_templ_t accelerator_t; + struct accelerator_t : accelerator_templ_t + { + HB_INTERNAL bool get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + const int *coords, + unsigned int num_coords) const; + }; + typedef accelerator_templ_t accelerator_subset_t; inline bool subset (hb_subset_plan_t *plan) const @@ -560,6 +576,7 @@ struct cff2 DEFINE_SIZE_STATIC (5); }; +struct cff2_accelerator_t : cff2::accelerator_t {}; } /* namespace OT */ #endif /* HB_OT_CFF2_TABLE_HH */ diff --git a/src/hb-ot-face.cc b/src/hb-ot-face.cc index 4a01c2c7a..fb10f5174 100644 --- a/src/hb-ot-face.cc +++ b/src/hb-ot-face.cc @@ -29,6 +29,7 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-kern-table.hh" #include "hb-ot-post-table.hh" diff --git a/src/hb-ot-face.hh b/src/hb-ot-face.hh index f3d641f8a..dec6d06a3 100644 --- a/src/hb-ot-face.hh +++ b/src/hb-ot-face.hh @@ -69,6 +69,7 @@ HB_OT_ACCELERATOR(OT, kern) \ HB_OT_ACCELERATOR(OT, glyf) \ HB_OT_ACCELERATOR(OT, cff1) \ + HB_OT_ACCELERATOR(OT, cff2) \ HB_OT_ACCELERATOR(OT, CBDT) \ /* */ diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 6877b6549..d6d5c5b37 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -124,9 +124,13 @@ hb_ot_get_glyph_extents (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data; + unsigned int num_coords; + const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); bool ret = ot_face->glyf->get_extents (glyph, extents); if (!ret) ret = ot_face->cff1->get_extents (glyph, extents); + if (!ret) + ret = ot_face->cff2->get_extents (glyph, extents, coords, num_coords); if (!ret) ret = ot_face->CBDT->get_extents (glyph, extents); // TODO Hook up side-bearings variations. diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 35fd9a7fe..fb98c318e 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1502,6 +1502,9 @@ struct VarRegionList struct VarData { + inline unsigned int get_region_index_count (void) const + { return regionIndices.len; } + inline unsigned int get_row_size (void) const { return shortCount + regionIndices.len; } @@ -1540,6 +1543,18 @@ struct VarData return delta; } + inline void get_scalars (int *coords, unsigned int coord_count, + const VarRegionList ®ions, + float *scalars /*OUT */, + unsigned int num_scalars) const + { + assert (num_scalars == regionIndices.len); + for (unsigned int i = 0; i < num_scalars; i++) + { + scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); + } + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1589,8 +1604,17 @@ struct VariationStore dataSets.sanitize (c, this)); } - inline unsigned int get_region_count (void) const - { return (this+regions).get_region_count (); } + inline unsigned int get_region_index_count (unsigned int ivs) const + { return (this+dataSets[ivs]).get_region_index_count (); } + + inline void get_scalars (unsigned int ivs, + int *coords, unsigned int coord_count, + float *scalars /*OUT*/, + unsigned int num_scalars) const + { + (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions, + &scalars[0], num_scalars); + } protected: HBUINT16 format; diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 18844163a..42b47f1f1 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -304,7 +304,7 @@ struct CFF1CSOpSet_Flatten : CFF1CSOpSet static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0) { for (unsigned int i = start_arg; i < env.argStack.get_count (); i++) - param.flatStr.encode_num (env.argStack[i]); + param.flatStr.encode_num (env.eval_arg (i)); SUPER::flush_args (env, param, start_arg); } diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 2bf27a692..0f4db1318 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -114,7 +114,7 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet for (unsigned int i = start_arg; i < env.argStack.get_count ();) { const BlendArg &arg = env.argStack[i]; - if (arg.blended ()) + if (arg.blending ()) { assert ((arg.numValues > 0) && (env.argStack.get_count () - start_arg >= arg.numValues)); flatten_blends (arg, i, env, param); @@ -135,7 +135,7 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet for (unsigned int j = 0; j < arg.numValues; j++) { const BlendArg &arg1 = env.argStack[i + j]; - assert (arg1.blended () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && + assert (arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && (arg1.deltas.len == env.get_region_count ())); param.flatStr.encode_num (arg1); } diff --git a/test/api/test-ot-extents-cff.c b/test/api/test-ot-extents-cff.c index 700396178..02ae06e19 100644 --- a/test/api/test-ot-extents-cff.c +++ b/test/api/test-ot-extents-cff.c @@ -54,12 +54,48 @@ test_extents_cff1 (void) hb_font_destroy (font); } +static void +test_extents_cff2 (void) +{ + hb_blob_t *blob = hb_blob_create_from_file ("fonts/AdobeVFPrototype.abc.otf"); + g_assert (blob); + hb_face_t *face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_glyph_extents_t extents; + hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 46); + g_assert_cmpint (extents.y_bearing, ==, 487); + g_assert_cmpint (extents.width, ==, 455); + g_assert_cmpint (extents.height, ==, -500); + + float coords[2] = { 600.0f, 50.0f }; + hb_font_set_var_coords_design (font, coords, 2); + result = hb_font_get_glyph_extents (font, 1, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 38); + g_assert_cmpint (extents.y_bearing, ==, 493); + g_assert_cmpint (extents.width, ==, 481); + g_assert_cmpint (extents.height, ==, -508); + + hb_font_destroy (font); +} + int main (int argc, char **argv) { hb_test_init (&argc, &argv); hb_test_add (test_extents_cff1); + hb_test_add (test_extents_cff2); return hb_test_run (); }