diff options
Diffstat (limited to 'Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh')
-rw-r--r-- | Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh | 676 |
1 files changed, 394 insertions, 282 deletions
diff --git a/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh b/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh index 571e50ea0c7..ce7ffa9d1f8 100644 --- a/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh +++ b/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-ot-glyf-table.hh @@ -34,8 +34,7 @@ #include "hb-ot-head-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-var-gvar-table.hh" - -#include <float.h> +#include "hb-draw.hh" namespace OT { @@ -148,7 +147,19 @@ struct glyf const hb_subset_plan_t *plan) { TRACE_SERIALIZE (this); + unsigned init_len = c->length (); for (const auto &_ : it) _.serialize (c, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } return_trace (true); } @@ -229,18 +240,18 @@ struct glyf { enum composite_glyph_flag_t { - ARG_1_AND_2_ARE_WORDS = 0x0001, - ARGS_ARE_XY_VALUES = 0x0002, - ROUND_XY_TO_GRID = 0x0004, - WE_HAVE_A_SCALE = 0x0008, - MORE_COMPONENTS = 0x0020, - WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, - WE_HAVE_A_TWO_BY_TWO = 0x0080, - WE_HAVE_INSTRUCTIONS = 0x0100, - USE_MY_METRICS = 0x0200, - OVERLAP_COMPOUND = 0x0400, - SCALED_COMPONENT_OFFSET = 0x0800, - UNSCALED_COMPONENT_OFFSET = 0x1000 + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000 }; unsigned int get_size () const @@ -361,7 +372,7 @@ struct glyf typedef const CompositeGlyphChain *__item_t__; composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : glyph (glyph_), current (current_) - { if (!in_range (current)) current = nullptr; } + { if (!check_range (current)) current = nullptr; } composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {} const CompositeGlyphChain &__item__ () const { return *current; } @@ -372,16 +383,16 @@ struct glyf const CompositeGlyphChain *possible = &StructAfter<CompositeGlyphChain, CompositeGlyphChain> (*current); - if (!in_range (possible)) { current = nullptr; return; } + if (!check_range (possible)) { current = nullptr; return; } current = possible; } bool operator != (const composite_iter_t& o) const { return glyph != o.glyph || current != o.current; } - bool in_range (const CompositeGlyphChain *composite) const + bool check_range (const CompositeGlyphChain *composite) const { - return glyph.in_range (composite, CompositeGlyphChain::min_size) - && glyph.in_range (composite, composite->get_size ()); + return glyph.check_range (composite, CompositeGlyphChain::min_size) + && glyph.check_range (composite, composite->get_size ()); } private: @@ -389,8 +400,29 @@ struct glyf __item_t__ current; }; + enum phantom_point_index_t + { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; + 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 +477,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 */ @@ -470,7 +490,6 @@ struct glyf unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0); glyph += 2 + num_instructions; - if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); unsigned int coord_bytes = 0; unsigned int coords_with_flags = 0; @@ -519,70 +538,53 @@ struct glyf dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); } - struct x_setter_t - { - void set (contour_point_t &point, float v) const { point.x = v; } - bool is_short (uint8_t flag) const { return flag & FLAG_X_SHORT; } - bool is_same (uint8_t flag) const { return flag & FLAG_X_SAME; } - }; - - struct y_setter_t - { - void set (contour_point_t &point, float v) const { point.y = v; } - bool is_short (uint8_t flag) const { return flag & FLAG_Y_SHORT; } - bool is_same (uint8_t flag) const { return flag & FLAG_Y_SAME; } - }; - - template <typename T> static bool read_points (const HBUINT8 *&p /* IN/OUT */, contour_point_vector_t &points_ /* IN/OUT */, - const hb_bytes_t &bytes) + const hb_bytes_t &bytes, + void (* setter) (contour_point_t &_, float v), + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) { - T coord_setter; float v = 0; - for (unsigned int i = 0; i < points_.length - PHANTOM_COUNT; i++) + for (unsigned i = 0; i < points_.length; i++) { uint8_t flag = points_[i].flag; - if (coord_setter.is_short (flag)) + if (flag & short_flag) { - if (unlikely (!bytes.in_range (p))) return false; - if (coord_setter.is_same (flag)) + if (unlikely (!bytes.check_range (p))) return false; + if (flag & same_flag) v += *p++; else v -= *p++; } else { - if (!coord_setter.is_same (flag)) + if (!(flag & same_flag)) { - if (unlikely (!bytes.in_range ((const HBUINT16 *) p))) return false; + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; v += *(const HBINT16 *) p; p += HBINT16::static_size; } } - coord_setter.set (points_[i], v); + setter (points_[i], v); } return true; } bool get_contour_points (contour_point_vector_t &points_ /* OUT */, - hb_vector_t<unsigned int> &end_points_ /* OUT */, - const bool phantom_only=false) const + bool phantom_only = false) const { const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header); int num_contours = header.numberOfContours; - if (unlikely (!bytes.in_range (&endPtsOfContours[num_contours + 1]))) return false; + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; - points_.resize (num_points + PHANTOM_COUNT); + points_.resize (num_points); for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); if (phantom_only) return true; - /* Read simple glyph points if !phantom_only */ - end_points_.resize (num_contours); - for (int i = 0; i < num_contours; i++) - end_points_[i] = endPtsOfContours[i]; + points_[endPtsOfContours[i]].is_end_point = true; /* Skip instructions */ const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1], @@ -591,12 +593,12 @@ struct glyf /* Read flags */ for (unsigned int i = 0; i < num_points; i++) { - if (unlikely (!bytes.in_range (p))) return false; + if (unlikely (!bytes.check_range (p))) return false; uint8_t flag = *p++; points_[i].flag = flag; if (flag & FLAG_REPEAT) { - if (unlikely (!bytes.in_range (p))) return false; + if (unlikely (!bytes.check_range (p))) return false; unsigned int repeat_count = *p++; while ((repeat_count-- > 0) && (++i < num_points)) points_[i].flag = flag; @@ -604,8 +606,10 @@ struct glyf } /* Read x & y coordinates */ - return (read_points<x_setter_t> (p, points_, bytes) && - read_points<y_setter_t> (p, points_, bytes)); + return (read_points (p, points_, bytes, + [] (contour_point_t &p, float v) { p.x = v; }, FLAG_X_SHORT, FLAG_X_SAME) && + read_points (p, points_, bytes, + [] (contour_point_t &p, float v) { p.y = v; }, FLAG_Y_SHORT, FLAG_Y_SAME)); } }; @@ -648,30 +652,10 @@ struct glyf /* Chop instructions off the end */ void drop_hints_bytes (hb_bytes_t &dest_start) const { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } - - bool get_contour_points (contour_point_vector_t &points_ /* OUT */, - hb_vector_t<unsigned int> &end_points_ /* OUT */, - const bool phantom_only=false) const - { - /* add one pseudo point for each component in composite glyph */ - unsigned int num_points = hb_len (get_iterator ()); - points_.resize (num_points + PHANTOM_COUNT); - for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); - return true; - } }; enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; - enum phantom_point_index_t - { - PHANTOM_LEFT = 0, - PHANTOM_RIGHT = 1, - PHANTOM_TOP = 2, - PHANTOM_BOTTOM = 3, - PHANTOM_COUNT = 4 - }; - public: composite_iter_t get_composite_iterator () const { @@ -706,39 +690,130 @@ struct glyf } } - /* for a simple glyph, return contour end points, flags, along with coordinate points - * for a composite glyph, return pseudo component points - * in both cases points trailed with four phantom points + /* Note: Recursively calls itself. + * all_points includes phantom points */ - bool get_contour_points (contour_point_vector_t &points_ /* OUT */, - hb_vector_t<unsigned int> &end_points_ /* OUT */, - const bool phantom_only=false) const + template<typename T> + bool get_points (T glyph_for_gid, hb_font_t *font, + contour_point_vector_t &all_points /* OUT */, + bool phantom_only = false, + unsigned int depth = 0) const { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t points; + switch (type) { - case COMPOSITE: return CompositeGlyph (*header, bytes).get_contour_points (points_, end_points_, phantom_only); - case SIMPLE: return SimpleGlyph (*header, bytes).get_contour_points (points_, end_points_, phantom_only); - default: - /* empty glyph */ - points_.resize (PHANTOM_COUNT); - for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); - return true; + case COMPOSITE: + { + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ()); + if (unlikely (!points.resize (num_points))) return false; + for (unsigned i = 0; i < points.length; i++) + points[i].init (); + break; + } + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + default: return false; /* empty glyph */ } - } - bool is_simple_glyph () const { return type == SIMPLE; } - bool is_composite_glyph () const { return type == COMPOSITE; } + hb_face_t *face = font->face; - bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init (); + int h_delta = (int) header->xMin - face->table.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + face->table.vmtx->get_side_bearing (gid); + unsigned h_adv = face->table.hmtx->get_advance (gid); + unsigned v_adv = face->table.vmtx->get_advance (gid); + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + if (unlikely (!face->table.gvar->apply_deltas_to_points (gid, font, points.as_array ()))) + return false; +#endif + + switch (type) { + case SIMPLE: + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + contour_point_vector_t comp_points; + if (unlikely (!glyph_for_gid (item.glyphIndex).get_points (glyph_for_gid, font, comp_points, phantom_only, depth + 1)) + || comp_points.length < PHANTOM_COUNT) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; + } + + all_points.extend (phantoms); + } break; + default: return false; + } + + if (depth == 0) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return true; + } + + bool get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const { if (type == EMPTY) return true; /* Empty glyph; zero extents. */ return header->get_extents (font, gid, extents); } - hb_bytes_t get_bytes () const { return bytes; } - const GlyphHeader &get_header () const { return *header; } + hb_bytes_t get_bytes () const { return bytes; } - Glyph (hb_bytes_t bytes_ = hb_bytes_t ()) : - bytes (bytes_), header (bytes.as<GlyphHeader> ()) + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), + header (bytes.as<GlyphHeader> ()) { int num_contours = header->numberOfContours; if (unlikely (num_contours == 0)) type = EMPTY; @@ -748,6 +823,7 @@ struct glyf protected: hb_bytes_t bytes; + hb_codepoint_t gid; const GlyphHeader *header; unsigned type; }; @@ -771,6 +847,7 @@ struct glyf glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face); num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); } void fini () @@ -779,223 +856,137 @@ struct glyf glyf_table.destroy (); } - enum phantom_point_index_t - { - PHANTOM_LEFT = 0, - PHANTOM_RIGHT = 1, - PHANTOM_TOP = 2, - PHANTOM_BOTTOM = 3, - PHANTOM_COUNT = 4 - }; - protected: - - void init_phantom_points (hb_codepoint_t gid, hb_array_t<contour_point_t> &phantoms /* IN/OUT */) const + template<typename T> + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const { - const Glyph &glyph = glyph_for_gid (gid); - int h_delta = (int) glyph.get_header ().xMin - face->table.hmtx->get_side_bearing (gid); - int v_orig = (int) glyph.get_header ().yMax + face->table.vmtx->get_side_bearing (gid); - unsigned int h_adv = face->table.hmtx->get_advance (gid); - unsigned int v_adv = face->table.vmtx->get_advance (gid); - - phantoms[PHANTOM_LEFT].x = h_delta; - phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; - phantoms[PHANTOM_TOP].y = v_orig; - phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; - } + /* Making this 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 for non-VF fonts can be considered */ + contour_point_vector_t all_points; - struct contour_bounds_t - { - contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points ([this] (hb_codepoint_t gid) -> const Glyph { return this->glyph_for_gid (gid); }, + font, all_points, phantom_only))) + return false; - void add (const contour_point_t &p) + if (consumer.is_consuming_contour_points ()) { - min_x = hb_min (min_x, p.x); - min_y = hb_min (min_y, p.y); - max_x = hb_max (max_x, p.x); - max_y = hb_max (max_y, p.y); + for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); } - bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } - - void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) - { - if (unlikely (empty ())) - { - extents->width = 0; - extents->x_bearing = 0; - extents->height = 0; - extents->y_bearing = 0; - return; - } - extents->x_bearing = font->em_scalef_x (min_x); - extents->width = font->em_scalef_x (max_x - min_x); - extents->y_bearing = font->em_scalef_y (max_y); - extents->height = font->em_scalef_y (min_y - max_y); - } + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i]; - protected: - float min_x, min_y, max_x, max_y; - }; + return true; + } + public: #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 + struct points_aggregator_t { - if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return false; - contour_point_vector_t points; - hb_vector_t<unsigned int> end_points; - const Glyph &glyph = glyph_for_gid (gid); - if (unlikely (!glyph.get_contour_points (points, end_points))) return false; - hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); - init_phantom_points (gid, phantoms); - if (unlikely (!face->table.gvar->apply_deltas_to_points (gid, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; - unsigned int comp_index = 0; - if (glyph.is_simple_glyph ()) - all_points.extend (points.as_array ()); - else if (glyph.is_composite_glyph ()) + struct contour_bounds_t { - 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)) - || comp_points.length < PHANTOM_COUNT) - return false; - - /* Copy phantom points from component if USE_MY_METRICS flag set */ - if (item.is_use_my_metrics ()) - for (unsigned int i = 0; i < PHANTOM_COUNT; i++) - phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } - /* Apply component transformation & translation */ - item.transform_points (comp_points); + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } - /* Apply translatation from gvar */ - comp_points.translate (points[comp_index]); + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } - if (item.is_anchored ()) + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) + { + if (unlikely (empty ())) { - unsigned int p1, p2; - item.get_anchor_points (p1, p2); - if (likely (p1 < all_points.length && p2 < comp_points.length)) - { - contour_point_t delta; - delta.init (all_points[p1].x - comp_points[p2].x, - all_points[p1].y - comp_points[p2].y); - - comp_points.translate (delta); - } + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; } - - all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); - - comp_index++; + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x - min_x); + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y - max_y); } - all_points.extend (phantoms); - } - else return false; + protected: + float min_x, min_y, max_x, max_y; + } bounds; - return true; - } - - bool get_points_bearing_applied (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) || - all_points.length < PHANTOM_COUNT)) return false; - - /* Undocumented rasterizer behavior: - * Shift points horizontally by the updated left side bearing - */ - contour_point_t delta; - delta.init (-all_points[all_points.length - PHANTOM_COUNT + PHANTOM_LEFT].x, 0.f); - if (delta.x) all_points.translate (delta); - return true; - } - - protected: - - 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 - { - contour_point_vector_t all_points; - if (!unlikely (get_points_bearing_applied (font, gid, all_points))) return false; - if (extents) + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) { - contour_bounds_t bounds; - for (unsigned int i = 0; i + PHANTOM_COUNT < all_points.length; i++) - bounds.add (all_points[i]); - bounds.get_extents (font, extents); + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); } - if (phantoms) - for (unsigned int i = 0; i < PHANTOM_COUNT; i++) - (*phantoms)[i] = all_points[all_points.length - PHANTOM_COUNT + i]; - return true; - } - bool get_var_metrics (hb_font_t *font, hb_codepoint_t gid, - contour_point_vector_t &phantoms) const - { return get_var_extents_and_phantoms (font, gid, nullptr, &phantoms); } + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } - 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); } -#endif + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; - public: -#ifndef HB_NO_VAR - unsigned int get_advance_var (hb_font_t *font, hb_codepoint_t gid, - bool is_vertical) const + unsigned int + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const { bool success = false; - contour_point_vector_t phantoms; - phantoms.resize (PHANTOM_COUNT); + contour_point_t phantoms[PHANTOM_COUNT]; if (likely (font->num_coords == face->table.gvar->get_axis_count ())) - success = get_var_metrics (font, gid, phantoms); + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); if (unlikely (!success)) - return is_vertical ? face->table.vmtx->get_advance (gid) : face->table.hmtx->get_advance (gid); + return is_vertical + ? face->table.vmtx->get_advance (gid) + : face->table.hmtx->get_advance (gid); - if (is_vertical) - return roundf (phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y); - else - return roundf (phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x); + return is_vertical + ? roundf (phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y) + : roundf (phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x); } int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const { hb_glyph_extents_t extents; - contour_point_vector_t phantoms; - phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_extents_and_phantoms (font, gid, &extents, &phantoms))) - return is_vertical ? face->table.vmtx->get_side_bearing (gid) : face->table.hmtx->get_side_bearing (gid); + contour_point_t phantoms[PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return is_vertical + ? face->table.vmtx->get_side_bearing (gid) + : face->table.hmtx->get_side_bearing (gid); - return is_vertical ? ceil (phantoms[PHANTOM_TOP].y) - extents.y_bearing : floor (phantoms[PHANTOM_LEFT].x); + return is_vertical + ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[PHANTOM_LEFT].x); } #endif bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const { + if (unlikely (gid >= num_glyphs)) return false; #ifndef HB_NO_VAR - unsigned int coord_count; - const int *coords = hb_font_get_var_coords_normalized (font, &coord_count); - if (coords && coord_count > 0 && coord_count == face->table.gvar->get_axis_count ()) - return get_extents_var (font, gid, extents); + if (font->num_coords && font->num_coords == face->table.gvar->get_axis_count ()) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); #endif - - if (unlikely (gid >= num_glyphs)) return false; - - return glyph_for_gid (gid).get_extents (font, gid, extents); + return glyph_for_gid (gid).get_extents (font, extents); } const Glyph @@ -1021,7 +1012,7 @@ struct glyf return Glyph (); Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, - end_offset - start_offset)); + end_offset - start_offset), gid); return needs_padding_removal ? glyph.trim_padding () : glyph; } @@ -1036,9 +1027,130 @@ struct glyf gids_to_retain->add (gid); for (auto &item : glyph_for_gid (gid).get_composite_iterator ()) - add_gid_and_children (item.glyphIndex, gids_to_retain, depth); + add_gid_and_children (item.glyphIndex, gids_to_retain, depth); } +#ifdef HB_EXPERIMENTAL_API + struct path_builder_t + { + hb_font_t *font; + draw_helper_t *draw_helper; + + struct optional_point_t + { + optional_point_t () { has_data = false; } + optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; } + + bool has_data; + float x; + float y; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + font = font_; + draw_helper = &draw_helper_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + /* Skip empty contours */ + if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data)) + return; + + bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE; + optional_point_t p (point.x, point.y); + if (!first_oncurve.has_data) + { + if (is_on_curve) + { + first_oncurve = p; + draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + } + else + { + if (first_offcurve.has_data) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve.has_data) + { + if (is_on_curve) + { + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve.has_data && last_offcurve.has_data) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (last_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (first_oncurve.has_data) + draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_helper->end_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } + }; + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const + { return get_points (font, gid, path_builder_t (font, draw_helper)); } +#endif + private: bool short_offset; unsigned int num_glyphs; @@ -1073,7 +1185,7 @@ struct glyf pad_length--; } - if (!unlikely (dest_glyph.length)) return_trace (true); + if (unlikely (!dest_glyph.length)) return_trace (true); /* update components gids */ for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) |