diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index a6f75bf68..d6bd111a8 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -520,6 +520,25 @@ hb_ot_color_palette_get_name_id hb_ot_font_set_funcs +
+hb-ot-glyph +hb_ot_glyph_decompose_funcs_t +hb_ot_glyph_decompose_close_path_func_t +hb_ot_glyph_decompose_conic_to_func_t +hb_ot_glyph_decompose_cubic_to_func_t +hb_ot_glyph_decompose_line_to_func_t +hb_ot_glyph_decompose_move_to_func_t +hb_ot_glyph_decompose +hb_ot_glyph_decompose_funcs_create +hb_ot_glyph_decompose_funcs_destroy +hb_ot_glyph_decompose_funcs_reference +hb_ot_glyph_decompose_funcs_set_close_path_func +hb_ot_glyph_decompose_funcs_set_conic_to_func +hb_ot_glyph_decompose_funcs_set_cubic_to_func +hb_ot_glyph_decompose_funcs_set_line_to_func +hb_ot_glyph_decompose_funcs_set_move_to_func +
+
hb-ot-name hb_ot_name_id_t diff --git a/src/Makefile.am b/src/Makefile.am index 10668ce6b..f2d2c1fae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -334,6 +334,7 @@ noinst_PROGRAMS = \ test-buffer-serialize \ test-ot-meta \ test-ot-name \ + test-ot-glyph \ test-ot-glyphname \ test-gpos-size-params \ test-gsub-would-substitute \ @@ -360,6 +361,10 @@ test_ot_name_SOURCES = test-ot-name.cc test_ot_name_CPPFLAGS = $(HBCFLAGS) test_ot_name_LDADD = libharfbuzz.la $(HBLIBS) +test_ot_glyph_SOURCES = test-ot-glyph.cc +test_ot_glyph_CPPFLAGS = $(HBCFLAGS) +test_ot_glyph_LDADD = libharfbuzz.la $(HBLIBS) + test_ot_glyphname_SOURCES = test-ot-glyphname.cc test_ot_glyphname_CPPFLAGS = $(HBCFLAGS) test_ot_glyphname_LDADD = libharfbuzz.la $(HBLIBS) diff --git a/src/Makefile.sources b/src/Makefile.sources index 17362ba70..58c9fda3b 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -73,6 +73,8 @@ HB_BASE_sources = \ hb-ot-font.cc \ hb-ot-gasp-table.hh \ hb-ot-glyf-table.hh \ + hb-ot-glyph.cc \ + hb-ot-glyph.hh \ hb-ot-hdmx-table.hh \ hb-ot-head-table.hh \ hb-ot-hhea-table.hh \ @@ -199,6 +201,7 @@ HB_BASE_headers = \ hb-ot-color.h \ hb-ot-deprecated.h \ hb-ot-font.h \ + hb-ot-glyph.h \ hb-ot-layout.h \ hb-ot-math.h \ hb-ot-meta.h \ diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc index 251a0654d..a7b82697d 100644 --- a/src/harfbuzz.cc +++ b/src/harfbuzz.cc @@ -14,6 +14,7 @@ #include "hb-ot-color.cc" #include "hb-ot-face.cc" #include "hb-ot-font.cc" +#include "hb-ot-glyph.cc" #include "hb-ot-layout.cc" #include "hb-ot-map.cc" #include "hb-ot-math.cc" diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc index 55abd11d6..5fb14f71b 100644 --- a/src/hb-ot-cff1-table.cc +++ b/src/hb-ot-cff1-table.cc @@ -30,6 +30,7 @@ #include "hb-ot-cff1-table.hh" #include "hb-cff1-interp-cs.hh" +#include "hb-ot-glyph.hh" using namespace CFF; @@ -342,6 +343,137 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph return true; } +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + const hb_ot_glyph_decompose_funcs_t *funcs_, void *user_data_, + point_t *delta_) + { + path_open = false; + cff = cff_; + font = font_; + funcs = funcs_; + user_data = user_data_; + delta = delta_; + } + ~cff1_path_param_t () { end_path (); } + + void start_path () { path_open = true; } + void end_path () { if (path_open) funcs->close_path (user_data); path_open = false; } + bool is_path_open () const { return path_open; } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + funcs->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()), + user_data); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + funcs->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()), + user_data); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + funcs->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()), + font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()), + font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ()), + user_data); + } + + bool path_open; + hb_font_t *font; + const hb_ot_glyph_decompose_funcs_t *funcs; + void *user_data; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.end_path (); + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) param.start_path (); + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) param.start_path (); + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data, + bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, param.funcs, param.user_data, true) + && _get_path (param.cff, param.font, accent, param.funcs, param.user_data, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*cff->charStrings)[glyph]; + interp.env.init (str, *cff, fd); + interp.env.set_in_seac (in_seac); + cff1_path_param_t param (cff, font, funcs, user_data, delta); + if (unlikely (!interp.interpret (param))) return false; + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, funcs, user_data); +} + struct get_seac_param_t { void init (const OT::cff1::accelerator_t *_cff) diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index 67f4db306..da3210303 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -1347,6 +1347,8 @@ struct cff1 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data) const; private: struct gname_t diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc index a2242b76f..194b08e65 100644 --- a/src/hb-ot-cff2-table.cc +++ b/src/hb-ot-cff2-table.cc @@ -30,6 +30,7 @@ #include "hb-ot-cff2-table.hh" #include "hb-cff2-interp-cs.hh" +#include "hb-ot-glyph.hh" using namespace CFF; @@ -142,5 +143,90 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, return true; } +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, const hb_ot_glyph_decompose_funcs_t *funcs_, void *user_data_) + { + path_open = false; + font = font_; + funcs = funcs_; + user_data = user_data_; + } + ~cff2_path_param_t () { end_path (); } + + void start_path () { path_open = true; } + void end_path () { if (path_open) funcs->close_path (user_data); path_open = false; } + bool is_path_open () const { return path_open; } + + void move_to (const point_t &p) + { + funcs->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ()), + user_data); + } + + void line_to (const point_t &p) + { + funcs->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ()), + user_data); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + funcs->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()), + font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()), + font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ()), + user_data); + } + + bool path_open; + hb_font_t *font; + const hb_ot_glyph_decompose_funcs_t *funcs; + void *user_data; +}; + +struct cff2_path_procs_path_t : path_procs_t +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.end_path (); + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) param.start_path (); + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) param.start_path (); + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff2_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_path_param_t param (font, funcs, user_data); + if (unlikely (!interp.interpret (param))) return false; + return true; +} #endif diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh index 8646cde58..55312287f 100644 --- a/src/hb-ot-cff2-table.hh +++ b/src/hb-ot-cff2-table.hh @@ -533,6 +533,8 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data) const; }; typedef accelerator_templ_t accelerator_subset_t; diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 3e7adcfc3..06eb303db 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -34,6 +34,7 @@ #include "hb-ot-head-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-var-gvar-table.hh" +#include "hb-ot-glyph.hh" #include @@ -391,6 +392,18 @@ struct glyf struct Glyph { + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_RESERVED1 = 0x40, + FLAG_RESERVED2 = 0x80 + }; + private: struct GlyphHeader { @@ -445,18 +458,6 @@ struct glyf return instructionLength; } - enum simple_glyph_flag_t - { - FLAG_ON_CURVE = 0x01, - FLAG_X_SHORT = 0x02, - FLAG_Y_SHORT = 0x04, - FLAG_REPEAT = 0x08, - FLAG_X_SAME = 0x10, - FLAG_Y_SAME = 0x20, - FLAG_RESERVED1 = 0x40, - FLAG_RESERVED2 = 0x80 - }; - const Glyph trim_padding () const { /* based on FontTools _g_l_y_f.py::trim */ @@ -582,7 +583,10 @@ struct glyf end_points_.resize (num_contours); for (int i = 0; i < num_contours; i++) + { end_points_[i] = endPtsOfContours[i]; + points_[end_points_[i]].is_end_point = true; + } /* Skip instructions */ const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], @@ -838,14 +842,13 @@ struct glyf float min_x, min_y, max_x, max_y; }; -#ifndef HB_NO_VAR /* Note: Recursively calls itself. * all_points includes phantom points */ - bool get_points_var (hb_codepoint_t gid, - const int *coords, unsigned int coord_count, - contour_point_vector_t &all_points /* OUT */, - unsigned int depth = 0) const + bool _get_points (hb_codepoint_t gid, + const int *coords, unsigned int coord_count, + contour_point_vector_t &all_points /* OUT */, + unsigned int depth = 0) const { if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return false; contour_point_vector_t points; @@ -854,18 +857,20 @@ struct glyf if (unlikely (!glyph.get_contour_points (points, end_points))) return false; hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (gid, phantoms); +#ifndef HB_NO_VAR if (unlikely (!face->table.gvar->apply_deltas_to_points (gid, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; +#endif - unsigned int comp_index = 0; if (glyph.is_simple_glyph ()) all_points.extend (points.as_array ()); else if (glyph.is_composite_glyph ()) { + unsigned int comp_index = 0; for (auto &item : glyph.get_composite_iterator ()) { contour_point_vector_t comp_points; - if (unlikely (!get_points_var (item.glyphIndex, coords, coord_count, - comp_points, depth)) + if (unlikely (!_get_points (item.glyphIndex, coords, coord_count, + comp_points, depth)) || comp_points.length < PHANTOM_COUNT) return false; @@ -906,9 +911,9 @@ struct glyf return true; } - bool get_points_bearing_applied (hb_font_t *font, hb_codepoint_t gid, contour_point_vector_t &all_points) const + bool get_points (hb_font_t *font, hb_codepoint_t gid, contour_point_vector_t &all_points) const { - if (unlikely (!get_points_var (gid, font->coords, font->num_coords, all_points) || + if (unlikely (!_get_points (gid, font->coords, font->num_coords, all_points) || all_points.length < PHANTOM_COUNT)) return false; /* Undocumented rasterizer behavior: @@ -920,14 +925,13 @@ struct glyf return true; } - protected: - +#ifndef HB_NO_VAR bool get_var_extents_and_phantoms (hb_font_t *font, hb_codepoint_t gid, - hb_glyph_extents_t *extents=nullptr /* OUT */, - contour_point_vector_t *phantoms=nullptr /* OUT */) const + hb_glyph_extents_t *extents /* OUT */, + contour_point_vector_t *phantoms /* OUT */) const { contour_point_vector_t all_points; - if (unlikely (!get_points_bearing_applied (font, gid, all_points))) return false; + if (unlikely (!get_points (font, gid, all_points))) return false; if (extents) { contour_bounds_t bounds; @@ -947,7 +951,7 @@ struct glyf bool get_extents_var (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const - { return get_var_extents_and_phantoms (font, gid, extents); } + { return get_var_extents_and_phantoms (font, gid, extents, nullptr); } #endif public: @@ -1039,6 +1043,66 @@ struct glyf add_gid_and_children (item.glyphIndex, gids_to_retain, depth); } + bool + get_path (hb_font_t *font, hb_codepoint_t gid, + const hb_ot_glyph_decompose_funcs_t *funcs, void *user_data) const + { + /* Making this completely alloc free is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path can be considered */ + contour_point_vector_t all_points; + if (unlikely (!get_points (font, gid, all_points))) return false; + hb_array_t points = all_points.sub_array (0, all_points.length - 4); + + unsigned contour_start = 0; + /* Learnt from https://github.com/opentypejs/opentype.js/blob/4e0bb99/src/tables/glyf.js#L222 */ + while (contour_start < points.length) + { + unsigned contour_length = 0; + for (unsigned i = contour_start; i < points.length; ++i) + { + contour_length++; + if (points[i].is_end_point) + break; + } + contour_point_t *curr = &points[contour_start + contour_length - 1]; + contour_point_t *next = &points[contour_start]; + + if (curr->flag & Glyph::FLAG_ON_CURVE) + funcs->move_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), user_data); + else + { + if (next->flag & Glyph::FLAG_ON_CURVE) + funcs->move_to (font->em_scalef_x (next->x), font->em_scalef_y (next->y), user_data); + else + /* If both first and last points are off-curve, start at their middle. */ + funcs->move_to (font->em_scalef_x ((curr->x + next->x) / 2.f), + font->em_scalef_y ((curr->y + next->y) / 2.f), user_data); + } + + for (unsigned i = 0; i < contour_length; ++i) + { + curr = next; + next = &points[contour_start + ((i + 1) % contour_length)]; + + if (curr->flag & Glyph::FLAG_ON_CURVE) + funcs->line_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), user_data); + else + { + float to_x, to_y; + if (next->flag & Glyph::FLAG_ON_CURVE) { to_x = next->x; to_y = next->y; } + else { to_x = (curr->x + next->x) / 2.f; to_y = (curr->y + next->y) / 2.f; } + funcs->conic_to (font->em_scalef_x (curr->x), font->em_scalef_y (curr->y), + font->em_scalef_x (to_x), font->em_scalef_y (to_y), user_data); + } + } + contour_start += contour_length; + funcs->close_path (user_data); + } + return true; + } + private: bool short_offset; unsigned int num_glyphs; diff --git a/src/hb-ot-glyph.cc b/src/hb-ot-glyph.cc new file mode 100644 index 000000000..0ef565a80 --- /dev/null +++ b/src/hb-ot-glyph.cc @@ -0,0 +1,223 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_GLYPH + +#include "hb-ot.h" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-glyph.hh" + +/** + * hb_ot_glyph_decompose_funcs_set_move_to_func: + * @funcs: decompose functions object + * @move_to: move-to callback + * + * Sets move-to callback to the decompose functions object. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_set_move_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_move_to_func_t move_to) +{ + if (unlikely (funcs == &Null (hb_ot_glyph_decompose_funcs_t))) return; + funcs->move_to = move_to; +} + +/** + * hb_ot_glyph_decompose_funcs_set_line_to_func: + * @funcs: decompose functions object + * @line_to: line-to callback + * + * Sets line-to callback to the decompose functions object. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_set_line_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_line_to_func_t line_to) +{ + if (unlikely (funcs == &Null (hb_ot_glyph_decompose_funcs_t))) return; + funcs->line_to = line_to; +} + +/** + * hb_ot_glyph_decompose_funcs_set_conic_to_func: + * @funcs: decompose functions object + * @move_to: conic-to callback + * + * Sets conic-to callback to the decompose functions object. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_set_conic_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_conic_to_func_t conic_to) +{ + if (unlikely (funcs == &Null (hb_ot_glyph_decompose_funcs_t))) return; + funcs->conic_to = conic_to; +} + +/** + * hb_ot_glyph_decompose_funcs_set_cubic_to_func: + * @funcs: decompose functions + * @cubic_to: cubic-to callback + * + * Sets cubic-to callback to the decompose functions object. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_set_cubic_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_cubic_to_func_t cubic_to) +{ + if (unlikely (funcs == &Null (hb_ot_glyph_decompose_funcs_t))) return; + funcs->cubic_to = cubic_to; +} + +/** + * hb_ot_glyph_decompose_funcs_set_close_path_func: + * @funcs: decompose functions object + * @close_path: close-path callback + * + * Sets close-path callback to the decompose functions object. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_set_close_path_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_close_path_func_t close_path) +{ + if (unlikely (funcs == &Null (hb_ot_glyph_decompose_funcs_t))) return; + funcs->close_path = close_path; +} + +static void +_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_conic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} +static void +_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED, + hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_close_path_nil (void *user_data HB_UNUSED) {} + +/** + * hb_ot_glyph_decompose_funcs_create: + * + * Creates a new decompose callbacks object. + * + * Since: REPLACEME + **/ +hb_ot_glyph_decompose_funcs_t * +hb_ot_glyph_decompose_funcs_create () +{ + hb_ot_glyph_decompose_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create ()))) + return const_cast (&Null (hb_ot_glyph_decompose_funcs_t)); + + funcs->move_to = (hb_ot_glyph_decompose_move_to_func_t) _move_to_nil; + funcs->line_to = (hb_ot_glyph_decompose_line_to_func_t) _line_to_nil; + funcs->conic_to = (hb_ot_glyph_decompose_conic_to_func_t) _conic_to_nil; + funcs->cubic_to = (hb_ot_glyph_decompose_cubic_to_func_t) _cubic_to_nil; + funcs->close_path = (hb_ot_glyph_decompose_close_path_func_t) _close_path_nil; + return funcs; +} + +/** + * hb_ot_glyph_decompose_funcs_reference: + * @funcs: decompose functions + * + * Add to callbacks object refcount. + * + * Returns: The same object. + * Since: REPLACEME + **/ +hb_ot_glyph_decompose_funcs_t * +hb_ot_glyph_decompose_funcs_reference (hb_ot_glyph_decompose_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_ot_glyph_decompose_funcs_destroy: + * @funcs: decompose functions + * + * Decreases refcount of callbacks object and deletes the object if it reaches + * to zero. + * + * Since: REPLACEME + **/ +void +hb_ot_glyph_decompose_funcs_destroy (hb_ot_glyph_decompose_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + free (funcs); +} + +/** + * hb_ot_glyph_decompose: + * @font: a font object + * @glyph: a glyph id + * @funcs: decompose callbacks object + * @user_data: parameter you like be passed to the callbacks when are called + * + * Decomposes a glyph. + * + * Returns: Whether the font had the glyph and the operation completed successfully. + * Since: REPLACEME + **/ +hb_bool_t +hb_ot_glyph_decompose (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, + void *user_data) +{ + if (unlikely (!funcs || funcs == &Null (hb_ot_glyph_decompose_funcs_t) || + glyph >= font->face->get_num_glyphs ())) + return false; + + if (font->face->table.glyf->get_path (font, glyph, funcs, user_data)) return true; +#ifndef HB_NO_CFF + if (font->face->table.cff1->get_path (font, glyph, funcs, user_data)) return true; + if (font->face->table.cff2->get_path (font, glyph, funcs, user_data)) return true; +#endif + + return false; +} + +#endif diff --git a/src/hb-ot-glyph.h b/src/hb-ot-glyph.h new file mode 100644 index 000000000..56238f88e --- /dev/null +++ b/src/hb-ot-glyph.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_GLYPH_H +#define HB_OT_GLYPH_H + +#include "hb.h" + +HB_BEGIN_DECLS + +typedef void (*hb_ot_glyph_decompose_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_ot_glyph_decompose_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_ot_glyph_decompose_conic_to_func_t) (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_ot_glyph_decompose_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_ot_glyph_decompose_close_path_func_t) (void *user_data); + +/** + * hb_ot_glyph_decompose_funcs_t: + * + * Glyph decompose callbacks. + * + * Since: REPLACEME + **/ +typedef struct hb_ot_glyph_decompose_funcs_t hb_ot_glyph_decompose_funcs_t; + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_set_move_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_move_to_func_t move_to); + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_set_line_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_move_to_func_t line_to); + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_set_conic_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_conic_to_func_t conic_to); + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_set_cubic_to_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_cubic_to_func_t cubic_to); + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_set_close_path_func (hb_ot_glyph_decompose_funcs_t *funcs, + hb_ot_glyph_decompose_close_path_func_t close_path); + +HB_EXTERN hb_ot_glyph_decompose_funcs_t * +hb_ot_glyph_decompose_funcs_create (void); + +HB_EXTERN hb_ot_glyph_decompose_funcs_t * +hb_ot_glyph_decompose_funcs_reference (hb_ot_glyph_decompose_funcs_t *funcs); + +HB_EXTERN void +hb_ot_glyph_decompose_funcs_destroy (hb_ot_glyph_decompose_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_ot_glyph_decompose (hb_font_t *font, hb_codepoint_t glyph, + const hb_ot_glyph_decompose_funcs_t *funcs, + void *user_data); + +HB_END_DECLS + +#endif /* HB_OT_GLYPH_H */ diff --git a/src/hb-ot-glyph.hh b/src/hb-ot-glyph.hh new file mode 100644 index 000000000..6b1acb0f0 --- /dev/null +++ b/src/hb-ot-glyph.hh @@ -0,0 +1,45 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_GLYPH_HH +#define HB_OT_GLYPH_HH + +#include "hb.hh" + +struct hb_ot_glyph_decompose_funcs_t +{ + hb_object_header_t header; + + hb_ot_glyph_decompose_move_to_func_t move_to; + hb_ot_glyph_decompose_line_to_func_t line_to; + hb_ot_glyph_decompose_conic_to_func_t conic_to; + hb_ot_glyph_decompose_cubic_to_func_t cubic_to; + hb_ot_glyph_decompose_close_path_func_t close_path; +}; + + +#endif /* HB_OT_GLYPH_HH */ diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 0a5465a9d..5f2ccec74 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -41,12 +41,14 @@ namespace OT { struct contour_point_t { - void init (float x_=0.f, float y_=0.f) { flag = 0; x = x_; y = y_; } + void init (float x_ = 0.f, float y_ = 0.f, bool is_end_point_ = false) + { flag = 0; x = x_; y = y_; is_end_point = is_end_point_; } void translate (const contour_point_t &p) { x += p.x; y += p.y; } uint8_t flag; float x, y; + bool is_end_point; }; struct contour_point_vector_t : hb_vector_t @@ -562,6 +564,7 @@ struct gvar const hb_array_t points, const hb_array_t end_points) const { + if (!coord_count) return true; if (unlikely (coord_count != gvar_table->axisCount)) return false; const GlyphVarData *var_data = gvar_table->get_glyph_var_data (glyph); diff --git a/src/hb-ot.h b/src/hb-ot.h index f2dbaa1b3..a66840bea 100644 --- a/src/hb-ot.h +++ b/src/hb-ot.h @@ -33,6 +33,7 @@ #include "hb-ot-color.h" #include "hb-ot-deprecated.h" #include "hb-ot-font.h" +#include "hb-ot-glyph.h" #include "hb-ot-layout.h" #include "hb-ot-math.h" #include "hb-ot-meta.h" diff --git a/src/test-ot-glyph.cc b/src/test-ot-glyph.cc new file mode 100644 index 000000000..63fb144a2 --- /dev/null +++ b/src/test-ot-glyph.cc @@ -0,0 +1,143 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb-ot.h" + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file(x) hb_blob_get_empty () +#endif + +#include +#include +#include + +struct user_data_t +{ + FILE *f; + hb_position_t ascender; +}; + +static void +move_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data) +{ + fprintf (user_data.f, "M%d,%d", to_x, user_data.ascender - to_y); +} + +static void +line_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data) +{ + fprintf (user_data.f, "L%d,%d", to_x, user_data.ascender - to_y); +} + +static void +conic_to (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + user_data_t &user_data) +{ + fprintf (user_data.f, "Q%d,%d %d,%d", control_x, user_data.ascender - control_y, + to_x, user_data.ascender - to_y); +} + +static void +cubic_to (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + user_data_t &user_data) +{ + fprintf (user_data.f, "C%d,%d %d,%d %d,%d", control1_x, user_data.ascender - control1_y, + control2_x, user_data.ascender - control2_y, + to_x, user_data.ascender - to_y); +} + +static void +close_path (user_data_t &user_data) +{ + fprintf (user_data.f, "Z"); +} + +int +main (int argc, char **argv) +{ + if (argc != 2) + { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file (argv[1]); + unsigned int num_faces = hb_face_count (blob); + if (num_faces == 0) + { + fprintf (stderr, "error: The file (%s) was corrupted, empty or not found", argv[1]); + exit (1); + } + + hb_ot_glyph_decompose_funcs_t *funcs = hb_ot_glyph_decompose_funcs_create (); + hb_ot_glyph_decompose_funcs_set_move_to_func (funcs, (hb_ot_glyph_decompose_move_to_func_t) move_to); + hb_ot_glyph_decompose_funcs_set_line_to_func (funcs, (hb_ot_glyph_decompose_line_to_func_t) line_to); + hb_ot_glyph_decompose_funcs_set_conic_to_func (funcs, (hb_ot_glyph_decompose_conic_to_func_t) conic_to); + hb_ot_glyph_decompose_funcs_set_cubic_to_func (funcs, (hb_ot_glyph_decompose_cubic_to_func_t) cubic_to); + hb_ot_glyph_decompose_funcs_set_close_path_func (funcs, (hb_ot_glyph_decompose_close_path_func_t) close_path); + + for (unsigned int face_index = 0; face_index < hb_face_count (blob); face_index++) + { + hb_face_t *face = hb_face_create (blob, face_index); + hb_font_t *font = hb_font_create (face); + unsigned int glyph_count = hb_face_get_glyph_count (face); + for (unsigned int gid = 0; gid < glyph_count; ++gid) + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + hb_glyph_extents_t extents = {0}; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + { + printf ("Skip gid: %d\n", gid); + continue; + } + + char name[100]; + sprintf (name, "%d.svg", gid); + FILE *f = fopen (name, "wb"); + fprintf (f, ""); + fclose (f); + } + hb_font_destroy (font); + hb_face_destroy (face); + } + + hb_ot_glyph_decompose_funcs_destroy (funcs); + + hb_blob_destroy (blob); + + return 0; +} diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 18a6468bc..868fe9dcb 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -38,6 +38,7 @@ TEST_PROGS = \ test-map \ test-object \ test-ot-face \ + test-ot-glyph \ test-ot-glyphname \ test-set \ test-shape \ diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c index 3d6fc9b68..4cc2024b1 100644 --- a/test/api/test-ot-face.c +++ b/test/api/test-ot-face.c @@ -121,6 +121,11 @@ test_face (hb_face_t *face, hb_ot_var_normalize_variations (face, NULL, 0, NULL, 0); hb_ot_var_normalize_coords (face, 0, NULL, NULL); + hb_ot_glyph_decompose_funcs_t *funcs = hb_ot_glyph_decompose_funcs_create (); + for (unsigned gid = 0; gid < 10; ++gid) + hb_ot_glyph_decompose (font, gid, funcs, NULL); + hb_ot_glyph_decompose_funcs_destroy (funcs); + hb_set_destroy (set); hb_font_destroy (font); } diff --git a/test/api/test-ot-glyph.c b/test/api/test-ot-glyph.c new file mode 100644 index 000000000..ee22ce28d --- /dev/null +++ b/test/api/test-ot-glyph.c @@ -0,0 +1,223 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * 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. + */ + +#include "hb-test.h" + +#include + +typedef struct user_data_t { + char *str; + unsigned size; + unsigned consumed; +} user_data_t; + +static void +move_to (hb_position_t to_x, hb_position_t to_y, user_data_t *user_data) +{ + user_data->consumed += snprintf (user_data->str + user_data->consumed, + user_data->size - user_data->consumed, + "M%d,%d", to_x, to_y); +} + +static void +line_to (hb_position_t to_x, hb_position_t to_y, user_data_t *user_data) +{ + user_data->consumed += snprintf (user_data->str + user_data->consumed, + user_data->size - user_data->consumed, + "L%d,%d", to_x, to_y); +} + +static void +conic_to (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + user_data_t *user_data) +{ + user_data->consumed += snprintf (user_data->str + user_data->consumed, + user_data->size - user_data->consumed, + "Q%d,%d %d,%d", + control_x, control_y, + to_x, to_y); +} + +static void +cubic_to (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + user_data_t *user_data) +{ + user_data->consumed += snprintf (user_data->str + user_data->consumed, + user_data->size - user_data->consumed, + "C%d,%d %d,%d %d,%d", + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); +} + +static void +close_path (user_data_t *user_data) +{ + user_data->consumed += snprintf (user_data->str + user_data->consumed, + user_data->size - user_data->consumed, + "Z"); +} + +static hb_ot_glyph_decompose_funcs_t *funcs; + +static void +test_hb_ot_glyph_empty (void) +{ + g_assert (!hb_ot_glyph_decompose (hb_font_get_empty (), 3, funcs, NULL)); +} + +static void +test_hb_ot_glyph_glyf (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSerifVariable-Roman-VVAR.abc.ttf"); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + + char str[1024] = {0}; + user_data_t user_data = { + .str = str, + .size = sizeof (str), + .consumed = 0 + }; + g_assert (!hb_ot_glyph_decompose (font, 4, funcs, &user_data)); + g_assert (hb_ot_glyph_decompose (font, 3, funcs, &user_data)); + char expected[] = "M275,442L275,442Q232,442 198,420Q164,397 145,353Q126,309 126,245L126,245" + "Q126,182 147,139Q167,95 204,73Q240,50 287,50L287,50Q330,50 367,70" + "Q404,90 427,128L427,128L451,116Q431,54 384,21Q336,-13 266,-13L266,-13Q198,-13 148,18" + "Q97,48 70,104Q43,160 43,236L43,236Q43,314 76,371Q108,427 160,457Q212,487 272,487L272,487" + "Q316,487 354,470Q392,453 417,424Q442,395 448,358L448,358Q441,321 403,321L403,321" + "Q378,321 367,334Q355,347 350,366L350,366L325,454L371,417Q346,430 321,436Q296,442 275,442Z"; + g_assert_cmpmem (str, user_data.consumed, expected, sizeof (expected) - 1); + + hb_variation_t var; + var.tag = HB_TAG ('w','g','h','t'); + var.value = 800; + hb_font_set_variations (font, &var, 1); + + char str2[1024] = {0}; + user_data_t user_data2 = { + .str = str2, + .size = sizeof (str2), + .consumed = 0 + }; + g_assert (hb_ot_glyph_decompose (font, 3, funcs, &user_data2)); + char expected2[] = "M323,448L323,448Q297,448 271,430Q244,412 227,371" + "Q209,330 209,261L209,261Q209,204 226,166Q242,127 273,107Q303,86 344,86L344,86Q378,86 404,101" + "Q430,115 451,137L451,137L488,103Q458,42 404,13Q350,-16 279,-16L279,-16Q211,-16 153,13Q95,41 60,99" + "Q25,156 25,241L25,241Q25,323 62,382Q99,440 163,471Q226,501 303,501L303,501Q357,501 399,481" + "Q440,460 464,426Q488,392 492,352L492,352Q475,297 420,297L420,297Q390,297 366,320" + "Q342,342 339,401L339,401L333,469L411,427Q387,438 368,443Q348,448 323,448Z"; + g_assert_cmpmem (str2, user_data2.consumed, expected2, sizeof (expected2) - 1); + + hb_font_destroy (font); +} + +static void +test_hb_ot_glyph_cff1 (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf"); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + + char str[1024] = {0}; + user_data_t user_data = { + .str = str, + .size = sizeof (str), + .consumed = 0 + }; + g_assert (hb_ot_glyph_decompose (font, 3, funcs, &user_data)); + char expected[] = "M203,367C227,440 248,512 268,588L272,588C293,512 314,440 338,367L369,267L172,267Z" + "M3,0L88,0L151,200L390,200L452,0L541,0L319,656L225,656Z" + "M300,653L342,694L201,861L143,806Z"; + g_assert_cmpmem (str, user_data.consumed, expected, sizeof (expected) - 1); + + hb_font_destroy (font); +} + +static void +test_hb_ot_glyph_cff2 (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf"); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + + char str[1024] = {0}; + user_data_t user_data = { + .str = str, + .size = sizeof (str), + .consumed = 0 + }; + g_assert (hb_ot_glyph_decompose (font, 3, funcs, &user_data)); + char expected[] = "M275,442C303,442 337,435 371,417L325,454L350,366" + "C357,341 370,321 403,321C428,321 443,333 448,358" + "C435,432 361,487 272,487C153,487 43,393 43,236" + "C43,83 129,-13 266,-13C360,-13 424,33 451,116L427,128" + "C396,78 345,50 287,50C193,50 126,119 126,245C126,373 188,442 275,442Z"; + g_assert_cmpmem (str, user_data.consumed, expected, sizeof (expected) - 1); + + hb_variation_t var; + var.tag = HB_TAG ('w','g','h','t'); + var.value = 800; + hb_font_set_variations (font, &var, 1); + + char str2[1024] = {0}; + user_data_t user_data2 = { + .str = str2, + .size = sizeof (str2), + .consumed = 0 + }; + g_assert (hb_ot_glyph_decompose (font, 3, funcs, &user_data2)); + char expected2[] = "M323,448C356,448 380,441 411,427L333,469L339,401" + "C343,322 379,297 420,297C458,297 480,314 492,352" + "C486,433 412,501 303,501C148,501 25,406 25,241" + "C25,70 143,-16 279,-16C374,-16 447,22 488,103L451,137" + "C423,107 390,86 344,86C262,86 209,148 209,261C209,398 271,448 323,448Z"; + g_assert_cmpmem (str2, user_data2.consumed, expected2, sizeof (expected2) - 1); + + hb_font_destroy (font); +} + +int +main (int argc, char **argv) +{ + funcs = hb_ot_glyph_decompose_funcs_create (); + hb_ot_glyph_decompose_funcs_set_move_to_func (funcs, (hb_ot_glyph_decompose_move_to_func_t) move_to); + hb_ot_glyph_decompose_funcs_set_line_to_func (funcs, (hb_ot_glyph_decompose_line_to_func_t) line_to); + hb_ot_glyph_decompose_funcs_set_conic_to_func (funcs, (hb_ot_glyph_decompose_conic_to_func_t) conic_to); + hb_ot_glyph_decompose_funcs_set_cubic_to_func (funcs, (hb_ot_glyph_decompose_cubic_to_func_t) cubic_to); + hb_ot_glyph_decompose_funcs_set_close_path_func (funcs, (hb_ot_glyph_decompose_close_path_func_t) close_path); + + hb_test_init (&argc, &argv); + hb_test_add (test_hb_ot_glyph_empty); + hb_test_add (test_hb_ot_glyph_glyf); + hb_test_add (test_hb_ot_glyph_cff1); + hb_test_add (test_hb_ot_glyph_cff2); + unsigned result = hb_test_run (); + + hb_ot_glyph_decompose_funcs_destroy (funcs); + return result; +}