From 100576b7b740cb65e808beba66d88f1c720715e5 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 25 Jun 2022 14:38:43 -0600 Subject: [PATCH] [glyf] Start splitting file --- src/Makefile.sources | 1 + src/OT/glyf/glyf.hh | 1338 +++++++++++++++++++++++++++++++++++++++ src/hb-ot-glyf-table.hh | 1333 +------------------------------------- src/hb-subset-plan.cc | 2 +- src/meson.build | 1 + 5 files changed, 1342 insertions(+), 1333 deletions(-) create mode 100644 src/OT/glyf/glyf.hh diff --git a/src/Makefile.sources b/src/Makefile.sources index 15dc0e9f5..c35a81623 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -88,6 +88,7 @@ HB_BASE_sources = \ hb-ot-layout-gdef-table.hh \ hb-ot-layout-gpos-table.hh \ hb-ot-layout-gsub-table.hh \ + OT/glyf/glyf.hh \ OT/Layout/GSUB/Common.hh \ OT/Layout/GSUB/Sequence.hh \ OT/Layout/GSUB/SingleSubstFormat1.hh \ diff --git a/src/OT/glyf/glyf.hh b/src/OT/glyf/glyf.hh new file mode 100644 index 000000000..80adfaa51 --- /dev/null +++ b/src/OT/glyf/glyf.hh @@ -0,0 +1,1338 @@ +#ifndef OT_GLYF_GLYF_HH +#define OT_GLYF_GLYF_HH + + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-draw.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + +#ifndef HB_MAX_COMPOSITE_OPERATIONS +#define HB_MAX_COMPOSITE_OPERATIONS 100000 +#endif + + +struct loca +{ + friend struct glyf; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + + +struct glyf +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + template + static bool + _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca) + { + unsigned num_offsets = padded_offsets.len () + 1; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d", + entry_size, num_offsets, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + hb_free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; + } + + template + static void + _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) + { + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, use_short_loca, 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); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t glyphs; + _populate_subset_glyphs (c->plan, &glyphs); + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&SubsetGlyph::padded_size) + ; + + unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); + bool use_short_loca = max_offset < 0x1FFFF; + + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); + if (!use_short_loca) { + padded_offsets = + + hb_iter (glyphs) + | hb_map (&SubsetGlyph::length) + ; + } + + + if (unlikely (c->serializer->in_error ())) return_trace (false); + return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, + padded_offsets, + use_short_loca))); + } + + template + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const + { + OT::glyf::glyf::accelerator_t glyf (plan->source); + + + hb_range (plan->num_output_glyphs ()) + | hb_map ([&] (hb_codepoint_t new_gid) + { + SubsetGlyph subset_glyph = {0}; + subset_glyph.new_gid = new_gid; + + /* should never fail: all old gids should be mapped */ + if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) + return subset_glyph; + + if (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + subset_glyph.source_glyph = Glyph (); + else + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + return subset_glyph; + }) + | hb_sink (glyphs) + ; + } + + static bool + _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) + { + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; + } + + struct CompositeGlyphChain + { + protected: + 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 + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; } + hb_codepoint_t get_glyph_index () const { return glyphIndex; } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + int tx, ty; + const HBINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + protected: + HBUINT16 flags; + HBGlyphID16 glyphIndex; + public: + DEFINE_SIZE_MIN (4); + }; + + struct composite_iter_t : hb_iter_with_fallback_t + { + typedef const CompositeGlyphChain *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_next (current_); + } + + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + const CompositeGlyphChain &__item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_next (&StructAtOffset (current, current_size)); + } + bool operator != (const composite_iter_t& o) const + { return glyph != o.glyph || current != o.current; } + + + void set_next (const CompositeGlyphChain *composite) + { + if (!glyph.check_range (composite, CompositeGlyphChain::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = composite->get_size (); + if (!glyph.check_range (composite, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = composite; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; + }; + + enum phantom_point_index_t + { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; + + struct accelerator_t; + + 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_OVERLAP_SIMPLE = 0x40, + FLAG_RESERVED2 = 0x80 + }; + + private: + struct GlyphHeader + { + bool has_data () const { return numberOfContours; } + + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); + }; + + struct SimpleGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const Glyph trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return Glyph (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); + return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (flags_offset + 1 > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + 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) + { + float v = 0; + for (unsigned i = 0; i < points_.length; i++) + { + uint8_t flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (!bytes.check_range (p))) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + setter (points_[i], v); + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.resize (num_points); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + /* Read flags */ + for (unsigned int i = 0; i < num_points; i++) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t flag = *p++; + points_[i].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (!bytes.check_range (p))) return false; + unsigned int repeat_count = *p++; + while ((repeat_count-- > 0) && (++i < num_points)) + points_[i].flag = flag; + } + } + + /* Read x & y coordinates */ + 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); + } + }; + + struct CompositeGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t get_iterator () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphChain *last = nullptr; + for (auto &item : get_iterator ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const Glyph trim_padding () const { return Glyph (bytes); } + + void drop_hints () + { + for (const auto &_ : get_iterator ()) + const_cast (_).drop_instructions_flag (); + } + + /* 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)); } + + void set_overlaps_flag () + { + CompositeGlyphChain& glyph_chain = const_cast ( + StructAfter (header)); + if (!bytes.check_range(&glyph_chain, CompositeGlyphChain::min_size)) + return; + glyph_chain.set_overlaps_flag (); + } + }; + + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).get_iterator (); + } + + const Glyph trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } + + void set_overlaps_flag () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + default: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } + } + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + 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: + { + /* 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; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_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 - + glyf_accelerator.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_side_bearing (gid) +#else + 0 +#endif + ; + unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_advance (gid) +#else + - font->face->get_upem () +#endif + ; + 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 + glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); +#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 (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ()) + .get_points (font, glyf_accelerator, 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: + all_points.extend (phantoms); + } + + 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, const accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), + header (bytes.as ()) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + hb_codepoint_t gid; + const GlyphHeader *header; + unsigned type; + }; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; +#ifndef HB_NO_VERTICAL + vmtx = nullptr; +#endif + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = face->table.loca.get_blob (); // Needs no destruct! + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; +#ifndef HB_NO_VERTICAL + vmtx = face->table.vmtx; +#endif + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + ~accelerator_t () + { + glyf_table.destroy (); + } + + bool has_data () const { return num_glyphs; } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this allocfree 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; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* 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]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + 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); + } + + 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) - extents->x_bearing; + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y) - extents->y_bearing; + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); + } + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (likely (font->num_coords == gvar->get_axis_count ())) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + + if (unlikely (!success)) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_advance (gid) : +#endif + hmtx->get_advance (gid); + + float result = is_vertical + ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y + : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_side_bearing (gid) : +#endif + hmtx->get_side_bearing (gid); + + return is_vertical + ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[PHANTOM_LEFT].x); + } +#endif + + public: + 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 + if (font->num_coords && font->num_coords == gvar->get_axis_count ()) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); +#endif + return glyph_for_gid (gid).get_extents (font, *this, extents); + } + + const Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return Glyph (); + + Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyph.trim_padding () : glyph; + } + + unsigned + add_gid_and_children (hb_codepoint_t gid, + hb_set_t *gids_to_retain, + unsigned depth = 0, + unsigned operation_count = 0) const + { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count; + if (unlikely (operation_count++ > HB_MAX_COMPOSITE_OPERATIONS)) return operation_count; + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return operation_count; + + gids_to_retain->add (gid); + + auto it = glyph_for_gid (gid).get_composite_iterator (); + while (it) + { + auto item = *(it++); + operation_count = + add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth, operation_count); + } + + return operation_count; + } + + struct path_builder_t + { + hb_font_t *font; + hb_draw_session_t *draw_session; + + 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_, hb_draw_session_t &draw_session_) + { + font = font_; + draw_session = &draw_session_; + 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) + { + 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_session->move_to (font->em_fscalef_x (p.x), font->em_fscalef_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_session->move_to (font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve.has_data) + { + if (is_on_curve) + { + draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), + font->em_fscalef_x (p.x), font->em_fscalef_y (p.y)); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), + font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_session->line_to (font->em_fscalef_x (p.x), font->em_fscalef_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_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), + font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve.has_data && first_oncurve.has_data) + draw_session->quadratic_to (font->em_fscalef_x (first_offcurve.x), font->em_fscalef_y (first_offcurve.y), + font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); + else if (last_offcurve.has_data && first_oncurve.has_data) + draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), + font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); + else if (first_oncurve.has_data) + draw_session->line_to (font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); + else if (first_offcurve.has_data) + { + float x = font->em_fscalef_x (first_offcurve.x), y = font->em_fscalef_x (first_offcurve.y); + draw_session->move_to (x, y); + draw_session->quadratic_to (x, y, x, y); + } + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_session->close_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, hb_draw_session_t &draw_session) const + { return get_points (font, gid, path_builder_t (font, draw_session)); } + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; +#ifndef HB_NO_VERTICAL + const vmtx_accelerator_t *vmtx; +#endif + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t loca_table; + hb_blob_ptr_t glyf_table; + }; + + struct SubsetGlyph + { + hb_codepoint_t new_gid; + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + bool use_short_loca, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = use_short_loca ? padding () : 0; + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid)) + const_cast (_).set_glyph_index (new_gid); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); + + return_trace (true); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } + }; + + protected: + UnsizedArrayOf + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t : glyf::glyf::accelerator_t { + glyf_accelerator_t (hb_face_t *face) : glyf::glyf::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HH */ diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 31dc46cee..c32ff7636 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -30,1337 +30,6 @@ #ifndef HB_OT_GLYF_TABLE_HH #define HB_OT_GLYF_TABLE_HH -#include "hb-open-type.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-hmtx-table.hh" -#include "hb-ot-var-gvar-table.hh" -#include "hb-draw.hh" - -namespace OT { - - -/* - * loca -- Index to Location - * https://docs.microsoft.com/en-us/typography/opentype/spec/loca - */ -#define HB_OT_TAG_loca HB_TAG('l','o','c','a') - -#ifndef HB_MAX_COMPOSITE_OPERATIONS -#define HB_MAX_COMPOSITE_OPERATIONS 100000 -#endif - - -struct loca -{ - friend struct glyf; - - static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; - - bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const - { - TRACE_SANITIZE (this); - return_trace (true); - } - - protected: - UnsizedArrayOf - dataZ; /* Location data. */ - public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it _MIN instead. */ -}; - - -/* - * glyf -- TrueType Glyph Data - * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf - */ -#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') - - -struct glyf -{ - static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; - - bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const - { - TRACE_SANITIZE (this); - /* Runtime checks as eager sanitizing each glyph is costy */ - return_trace (true); - } - - template - static bool - _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca) - { - unsigned num_offsets = padded_offsets.len () + 1; - unsigned entry_size = use_short_loca ? 2 : 4; - char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); - - if (unlikely (!loca_prime_data)) return false; - - DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d", - entry_size, num_offsets, entry_size * num_offsets); - - if (use_short_loca) - _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); - else - _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); - - hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, - entry_size * num_offsets, - HB_MEMORY_MODE_WRITABLE, - loca_prime_data, - hb_free); - - bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) - && _add_head_and_set_loca_version (plan, use_short_loca); - - hb_blob_destroy (loca_blob); - return result; - } - - template - static void - _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) - { - unsigned int offset = 0; - dest << 0; - + it - | hb_map ([=, &offset] (unsigned int padded_size) - { - offset += padded_size; - DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); - return offset >> right_shift; - }) - | hb_sink (dest) - ; - } - - /* requires source of SubsetGlyph complains the identifier isn't declared */ - template - bool serialize (hb_serialize_context_t *c, - Iterator it, - bool use_short_loca, - const hb_subset_plan_t *plan) - { - TRACE_SERIALIZE (this); - unsigned init_len = c->length (); - for (const auto &_ : it) _.serialize (c, use_short_loca, 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); - } - - /* Byte region(s) per glyph to output - unpadded, hints removed if so requested - If we fail to process a glyph we produce an empty (0-length) glyph */ - bool subset (hb_subset_context_t *c) const - { - TRACE_SUBSET (this); - - glyf *glyf_prime = c->serializer->start_embed (); - if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); - - hb_vector_t glyphs; - _populate_subset_glyphs (c->plan, &glyphs); - - auto padded_offsets = - + hb_iter (glyphs) - | hb_map (&SubsetGlyph::padded_size) - ; - - unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); - bool use_short_loca = max_offset < 0x1FFFF; - - - glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); - if (!use_short_loca) { - padded_offsets = - + hb_iter (glyphs) - | hb_map (&SubsetGlyph::length) - ; - } - - - if (unlikely (c->serializer->in_error ())) return_trace (false); - return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, - padded_offsets, - use_short_loca))); - } - - template - void - _populate_subset_glyphs (const hb_subset_plan_t *plan, - hb_vector_t *glyphs /* OUT */) const - { - OT::glyf::accelerator_t glyf (plan->source); - - + hb_range (plan->num_output_glyphs ()) - | hb_map ([&] (hb_codepoint_t new_gid) - { - SubsetGlyph subset_glyph = {0}; - subset_glyph.new_gid = new_gid; - - /* should never fail: all old gids should be mapped */ - if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) - return subset_glyph; - - if (new_gid == 0 && - !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) - subset_glyph.source_glyph = Glyph (); - else - subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); - if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) - subset_glyph.drop_hints_bytes (); - else - subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); - return subset_glyph; - }) - | hb_sink (glyphs) - ; - } - - static bool - _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) - { - hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); - hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); - hb_blob_destroy (head_blob); - - if (unlikely (!head_prime_blob)) - return false; - - head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); - head_prime->indexToLocFormat = use_short_loca ? 0 : 1; - bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); - - hb_blob_destroy (head_prime_blob); - return success; - } - - struct CompositeGlyphChain - { - protected: - 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 - }; - - public: - unsigned int get_size () const - { - unsigned int size = min_size; - /* arg1 and 2 are int16 */ - if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; - /* arg1 and 2 are int8 */ - else size += 2; - - /* One x 16 bit (scale) */ - if (flags & WE_HAVE_A_SCALE) size += 2; - /* Two x 16 bit (xscale, yscale) */ - else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; - /* Four x 16 bit (xscale, scale01, scale10, yscale) */ - else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; - - return size; - } - - void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; } - hb_codepoint_t get_glyph_index () const { return glyphIndex; } - - void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } - void set_overlaps_flag () - { - flags = (uint16_t) flags | OVERLAP_COMPOUND; - } - - bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } - - bool has_more () const { return flags & MORE_COMPONENTS; } - bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } - bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } - void get_anchor_points (unsigned int &point1, unsigned int &point2) const - { - const HBUINT8 *p = &StructAfter (glyphIndex); - if (flags & ARG_1_AND_2_ARE_WORDS) - { - point1 = ((const HBUINT16 *) p)[0]; - point2 = ((const HBUINT16 *) p)[1]; - } - else - { - point1 = p[0]; - point2 = p[1]; - } - } - - void transform_points (contour_point_vector_t &points) const - { - float matrix[4]; - contour_point_t trans; - if (get_transformation (matrix, trans)) - { - if (scaled_offsets ()) - { - points.translate (trans); - points.transform (matrix); - } - else - { - points.transform (matrix); - points.translate (trans); - } - } - } - - protected: - bool scaled_offsets () const - { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } - - bool get_transformation (float (&matrix)[4], contour_point_t &trans) const - { - matrix[0] = matrix[3] = 1.f; - matrix[1] = matrix[2] = 0.f; - - int tx, ty; - const HBINT8 *p = &StructAfter (glyphIndex); - if (flags & ARG_1_AND_2_ARE_WORDS) - { - tx = *(const HBINT16 *) p; - p += HBINT16::static_size; - ty = *(const HBINT16 *) p; - p += HBINT16::static_size; - } - else - { - tx = *p++; - ty = *p++; - } - if (is_anchored ()) tx = ty = 0; - - trans.init ((float) tx, (float) ty); - - { - const F2DOT14 *points = (const F2DOT14 *) p; - if (flags & WE_HAVE_A_SCALE) - { - matrix[0] = matrix[3] = points[0].to_float (); - return true; - } - else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) - { - matrix[0] = points[0].to_float (); - matrix[3] = points[1].to_float (); - return true; - } - else if (flags & WE_HAVE_A_TWO_BY_TWO) - { - matrix[0] = points[0].to_float (); - matrix[1] = points[1].to_float (); - matrix[2] = points[2].to_float (); - matrix[3] = points[3].to_float (); - return true; - } - } - return tx || ty; - } - - protected: - HBUINT16 flags; - HBGlyphID16 glyphIndex; - public: - DEFINE_SIZE_MIN (4); - }; - - struct composite_iter_t : hb_iter_with_fallback_t - { - typedef const CompositeGlyphChain *__item_t__; - composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : - glyph (glyph_), current (nullptr), current_size (0) - { - set_next (current_); - } - - composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} - - const CompositeGlyphChain &__item__ () const { return *current; } - bool __more__ () const { return current; } - void __next__ () - { - if (!current->has_more ()) { current = nullptr; return; } - - set_next (&StructAtOffset (current, current_size)); - } - bool operator != (const composite_iter_t& o) const - { return glyph != o.glyph || current != o.current; } - - - void set_next (const CompositeGlyphChain *composite) - { - if (!glyph.check_range (composite, CompositeGlyphChain::min_size)) - { - current = nullptr; - current_size = 0; - return; - } - unsigned size = composite->get_size (); - if (!glyph.check_range (composite, size)) - { - current = nullptr; - current_size = 0; - return; - } - - current = composite; - current_size = size; - } - - private: - hb_bytes_t glyph; - __item_t__ current; - unsigned current_size; - }; - - enum phantom_point_index_t - { - PHANTOM_LEFT = 0, - PHANTOM_RIGHT = 1, - PHANTOM_TOP = 2, - PHANTOM_BOTTOM = 3, - PHANTOM_COUNT = 4 - }; - - struct accelerator_t; - - 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_OVERLAP_SIMPLE = 0x40, - FLAG_RESERVED2 = 0x80 - }; - - private: - struct GlyphHeader - { - bool has_data () const { return numberOfContours; } - - bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, - hb_codepoint_t gid, hb_glyph_extents_t *extents) const - { - /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ - /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ - extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); - extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); - extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); - extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); - - return true; - } - - HBINT16 numberOfContours; - /* If the number of contours is - * greater than or equal to zero, - * this is a simple glyph; if negative, - * this is a composite glyph. */ - FWORD xMin; /* Minimum x for coordinate data. */ - FWORD yMin; /* Minimum y for coordinate data. */ - FWORD xMax; /* Maximum x for coordinate data. */ - FWORD yMax; /* Maximum y for coordinate data. */ - public: - DEFINE_SIZE_STATIC (10); - }; - - struct SimpleGlyph - { - const GlyphHeader &header; - hb_bytes_t bytes; - SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : - header (header_), bytes (bytes_) {} - - unsigned int instruction_len_offset () const - { return GlyphHeader::static_size + 2 * header.numberOfContours; } - - unsigned int length (unsigned int instruction_len) const - { return instruction_len_offset () + 2 + instruction_len; } - - unsigned int instructions_length () const - { - unsigned int instruction_length_offset = instruction_len_offset (); - if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; - - const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); - /* Out of bounds of the current glyph */ - if (unlikely (length (instructionLength) > bytes.length)) return 0; - return instructionLength; - } - - const Glyph trim_padding () const - { - /* based on FontTools _g_l_y_f.py::trim */ - const uint8_t *glyph = (uint8_t*) bytes.arrayZ; - const uint8_t *glyph_end = glyph + bytes.length; - /* simple glyph w/contours, possibly trimmable */ - glyph += instruction_len_offset (); - - if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); - unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; - unsigned int num_instructions = StructAtOffset (glyph, 0); - - glyph += 2 + num_instructions; - - unsigned int coord_bytes = 0; - unsigned int coords_with_flags = 0; - while (glyph < glyph_end) - { - uint8_t flag = *glyph; - glyph++; - - unsigned int repeat = 1; - if (flag & FLAG_REPEAT) - { - if (unlikely (glyph >= glyph_end)) return Glyph (); - repeat = *glyph + 1; - glyph++; - } - - unsigned int xBytes, yBytes; - xBytes = yBytes = 0; - if (flag & FLAG_X_SHORT) xBytes = 1; - else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; - - if (flag & FLAG_Y_SHORT) yBytes = 1; - else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; - - coord_bytes += (xBytes + yBytes) * repeat; - coords_with_flags += repeat; - if (coords_with_flags >= num_coordinates) break; - } - - if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); - return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); - } - - /* zero instruction length */ - void drop_hints () - { - GlyphHeader &glyph_header = const_cast (header); - (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; - } - - void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const - { - unsigned int instructions_len = instructions_length (); - unsigned int glyph_length = length (instructions_len); - dest_start = bytes.sub_array (0, glyph_length - instructions_len); - dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); - } - - void set_overlaps_flag () - { - if (unlikely (!header.numberOfContours)) return; - - unsigned flags_offset = length (instructions_length ()); - if (unlikely (flags_offset + 1 > bytes.length)) return; - - HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); - first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; - } - - static bool read_points (const HBUINT8 *&p /* IN/OUT */, - contour_point_vector_t &points_ /* IN/OUT */, - 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) - { - float v = 0; - for (unsigned i = 0; i < points_.length; i++) - { - uint8_t flag = points_[i].flag; - if (flag & short_flag) - { - if (unlikely (!bytes.check_range (p))) return false; - if (flag & same_flag) - v += *p++; - else - v -= *p++; - } - else - { - if (!(flag & same_flag)) - { - if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; - v += *(const HBINT16 *) p; - p += HBINT16::static_size; - } - } - setter (points_[i], v); - } - return true; - } - - bool get_contour_points (contour_point_vector_t &points_ /* OUT */, - bool phantom_only = false) const - { - const HBUINT16 *endPtsOfContours = &StructAfter (header); - int num_contours = header.numberOfContours; - if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; - unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; - - points_.resize (num_points); - for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); - if (phantom_only) return true; - - for (int i = 0; i < num_contours; i++) - points_[endPtsOfContours[i]].is_end_point = true; - - /* Skip instructions */ - const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], - endPtsOfContours[num_contours]); - - /* Read flags */ - for (unsigned int i = 0; i < num_points; i++) - { - if (unlikely (!bytes.check_range (p))) return false; - uint8_t flag = *p++; - points_[i].flag = flag; - if (flag & FLAG_REPEAT) - { - if (unlikely (!bytes.check_range (p))) return false; - unsigned int repeat_count = *p++; - while ((repeat_count-- > 0) && (++i < num_points)) - points_[i].flag = flag; - } - } - - /* Read x & y coordinates */ - 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); - } - }; - - struct CompositeGlyph - { - const GlyphHeader &header; - hb_bytes_t bytes; - CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : - header (header_), bytes (bytes_) {} - - composite_iter_t get_iterator () const - { return composite_iter_t (bytes, &StructAfter (header)); } - - unsigned int instructions_length (hb_bytes_t bytes) const - { - unsigned int start = bytes.length; - unsigned int end = bytes.length; - const CompositeGlyphChain *last = nullptr; - for (auto &item : get_iterator ()) - last = &item; - if (unlikely (!last)) return 0; - - if (last->has_instructions ()) - start = (char *) last - &bytes + last->get_size (); - if (unlikely (start > end)) return 0; - return end - start; - } - - /* Trimming for composites not implemented. - * If removing hints it falls out of that. */ - const Glyph trim_padding () const { return Glyph (bytes); } - - void drop_hints () - { - for (const auto &_ : get_iterator ()) - const_cast (_).drop_instructions_flag (); - } - - /* 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)); } - - void set_overlaps_flag () - { - CompositeGlyphChain& glyph_chain = const_cast ( - StructAfter (header)); - if (!bytes.check_range(&glyph_chain, CompositeGlyphChain::min_size)) - return; - glyph_chain.set_overlaps_flag (); - } - }; - - enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; - - public: - composite_iter_t get_composite_iterator () const - { - if (type != COMPOSITE) return composite_iter_t (); - return CompositeGlyph (*header, bytes).get_iterator (); - } - - const Glyph trim_padding () const - { - switch (type) { - case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); - case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); - default: return bytes; - } - } - - void drop_hints () - { - switch (type) { - case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; - case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; - default: return; - } - } - - void set_overlaps_flag () - { - switch (type) { - case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; - case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; - default: return; - } - } - - void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const - { - switch (type) { - case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; - case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; - default: return; - } - } - - /* Note: Recursively calls itself. - * all_points includes phantom points - */ - bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, - 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: - { - /* 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; - } - - /* Init phantom points */ - if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; - hb_array_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 - - glyf_accelerator.hmtx->get_side_bearing (gid); - int v_orig = (int) header->yMax + -#ifndef HB_NO_VERTICAL - glyf_accelerator.vmtx->get_side_bearing (gid) -#else - 0 -#endif - ; - unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); - unsigned v_adv = -#ifndef HB_NO_VERTICAL - glyf_accelerator.vmtx->get_advance (gid) -#else - - font->face->get_upem () -#endif - ; - 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 - glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); -#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 (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ()) - .get_points (font, glyf_accelerator, 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: - all_points.extend (phantoms); - } - - 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, const accelerator_t &glyf_accelerator, - hb_glyph_extents_t *extents) const - { - if (type == EMPTY) return true; /* Empty glyph; zero extents. */ - return header->get_extents (font, glyf_accelerator, gid, extents); - } - - hb_bytes_t get_bytes () const { return bytes; } - - Glyph (hb_bytes_t bytes_ = hb_bytes_t (), - hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), - header (bytes.as ()) - { - int num_contours = header->numberOfContours; - if (unlikely (num_contours == 0)) type = EMPTY; - else if (num_contours > 0) type = SIMPLE; - else type = COMPOSITE; /* negative numbers */ - } - - protected: - hb_bytes_t bytes; - hb_codepoint_t gid; - const GlyphHeader *header; - unsigned type; - }; - - struct accelerator_t - { - accelerator_t (hb_face_t *face) - { - short_offset = false; - num_glyphs = 0; - loca_table = nullptr; - glyf_table = nullptr; -#ifndef HB_NO_VAR - gvar = nullptr; -#endif - hmtx = nullptr; -#ifndef HB_NO_VERTICAL - vmtx = nullptr; -#endif - const OT::head &head = *face->table.head; - if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - return; - short_offset = 0 == head.indexToLocFormat; - - loca_table = face->table.loca.get_blob (); // Needs no destruct! - glyf_table = hb_sanitize_context_t ().reference_table (face); -#ifndef HB_NO_VAR - gvar = face->table.gvar; -#endif - hmtx = face->table.hmtx; -#ifndef HB_NO_VERTICAL - vmtx = face->table.vmtx; -#endif - - num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; - num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); - } - ~accelerator_t () - { - glyf_table.destroy (); - } - - bool has_data () const { return num_glyphs; } - - protected: - template - bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const - { - if (gid >= num_glyphs) return false; - - /* Making this allocfree 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; - - bool phantom_only = !consumer.is_consuming_contour_points (); - if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) - return false; - - if (consumer.is_consuming_contour_points ()) - { - for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) - consumer.consume_point (all_points[point_index]); - consumer.points_end (); - } - - /* 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]; - - return true; - } - -#ifndef HB_NO_VAR - struct points_aggregator_t - { - hb_font_t *font; - hb_glyph_extents_t *extents; - contour_point_t *phantoms; - - struct contour_bounds_t - { - contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } - - 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); - } - - 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) - extents->x_bearing; - extents->y_bearing = font->em_scalef_y (max_y); - extents->height = font->em_scalef_y (min_y) - extents->y_bearing; - } - - protected: - float min_x, min_y, max_x, max_y; - } bounds; - - points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) - { - font = font_; - extents = extents_; - phantoms = phantoms_; - if (extents) bounds = contour_bounds_t (); - } - - void consume_point (const contour_point_t &point) { bounds.add (point); } - void points_end () { bounds.get_extents (font, extents); } - - bool is_consuming_contour_points () { return extents; } - contour_point_t *get_phantoms_sink () { return phantoms; } - }; - - public: - unsigned - get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const - { - if (unlikely (gid >= num_glyphs)) return 0; - - bool success = false; - - contour_point_t phantoms[PHANTOM_COUNT]; - if (likely (font->num_coords == gvar->get_axis_count ())) - success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); - - if (unlikely (!success)) - return -#ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_advance (gid) : -#endif - hmtx->get_advance (gid); - - float result = is_vertical - ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y - : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; - return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); - } - - int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const - { - if (unlikely (gid >= num_glyphs)) return 0; - - hb_glyph_extents_t extents; - - contour_point_t phantoms[PHANTOM_COUNT]; - if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) - return -#ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_side_bearing (gid) : -#endif - hmtx->get_side_bearing (gid); - - return is_vertical - ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing - : floorf (phantoms[PHANTOM_LEFT].x); - } -#endif - - public: - 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 - if (font->num_coords && font->num_coords == gvar->get_axis_count ()) - return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); -#endif - return glyph_for_gid (gid).get_extents (font, *this, extents); - } - - const Glyph - glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const - { - if (unlikely (gid >= num_glyphs)) return Glyph (); - - unsigned int start_offset, end_offset; - - if (short_offset) - { - const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; - start_offset = 2 * offsets[gid]; - end_offset = 2 * offsets[gid + 1]; - } - else - { - const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; - start_offset = offsets[gid]; - end_offset = offsets[gid + 1]; - } - - if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) - return Glyph (); - - Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, - end_offset - start_offset), gid); - return needs_padding_removal ? glyph.trim_padding () : glyph; - } - - unsigned - add_gid_and_children (hb_codepoint_t gid, - hb_set_t *gids_to_retain, - unsigned depth = 0, - unsigned operation_count = 0) const - { - if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count; - if (unlikely (operation_count++ > HB_MAX_COMPOSITE_OPERATIONS)) return operation_count; - /* Check if is already visited */ - if (gids_to_retain->has (gid)) return operation_count; - - gids_to_retain->add (gid); - - auto it = glyph_for_gid (gid).get_composite_iterator (); - while (it) - { - auto item = *(it++); - operation_count = - add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth, operation_count); - } - - return operation_count; - } - - struct path_builder_t - { - hb_font_t *font; - hb_draw_session_t *draw_session; - - 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_, hb_draw_session_t &draw_session_) - { - font = font_; - draw_session = &draw_session_; - 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) - { - 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_session->move_to (font->em_fscalef_x (p.x), font->em_fscalef_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_session->move_to (font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); - } - else - first_offcurve = p; - } - } - else - { - if (last_offcurve.has_data) - { - if (is_on_curve) - { - draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), - font->em_fscalef_x (p.x), font->em_fscalef_y (p.y)); - last_offcurve = optional_point_t (); - } - else - { - optional_point_t mid = last_offcurve.lerp (p, .5f); - draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), - font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); - last_offcurve = p; - } - } - else - { - if (is_on_curve) - draw_session->line_to (font->em_fscalef_x (p.x), font->em_fscalef_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_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), - font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y)); - last_offcurve = optional_point_t (); - /* now check the rest */ - } - - if (first_offcurve.has_data && first_oncurve.has_data) - draw_session->quadratic_to (font->em_fscalef_x (first_offcurve.x), font->em_fscalef_y (first_offcurve.y), - font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); - else if (last_offcurve.has_data && first_oncurve.has_data) - draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y), - font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); - else if (first_oncurve.has_data) - draw_session->line_to (font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y)); - else if (first_offcurve.has_data) - { - float x = font->em_fscalef_x (first_offcurve.x), y = font->em_fscalef_x (first_offcurve.y); - draw_session->move_to (x, y); - draw_session->quadratic_to (x, y, x, y); - } - - /* Getting ready for the next contour */ - first_oncurve = first_offcurve = last_offcurve = optional_point_t (); - draw_session->close_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, hb_draw_session_t &draw_session) const - { return get_points (font, gid, path_builder_t (font, draw_session)); } - -#ifndef HB_NO_VAR - const gvar_accelerator_t *gvar; -#endif - const hmtx_accelerator_t *hmtx; -#ifndef HB_NO_VERTICAL - const vmtx_accelerator_t *vmtx; -#endif - - private: - bool short_offset; - unsigned int num_glyphs; - hb_blob_ptr_t loca_table; - hb_blob_ptr_t glyf_table; - }; - - struct SubsetGlyph - { - hb_codepoint_t new_gid; - hb_codepoint_t old_gid; - Glyph source_glyph; - hb_bytes_t dest_start; /* region of source_glyph to copy first */ - hb_bytes_t dest_end; /* region of source_glyph to copy second */ - - bool serialize (hb_serialize_context_t *c, - bool use_short_loca, - const hb_subset_plan_t *plan) const - { - TRACE_SERIALIZE (this); - - hb_bytes_t dest_glyph = dest_start.copy (c); - dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); - unsigned int pad_length = use_short_loca ? padding () : 0; - DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); - - HBUINT8 pad; - pad = 0; - while (pad_length > 0) - { - c->embed (pad); - pad_length--; - } - - if (unlikely (!dest_glyph.length)) return_trace (true); - - /* update components gids */ - for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) - { - hb_codepoint_t new_gid; - if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid)) - const_cast (_).set_glyph_index (new_gid); - } - - if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) - Glyph (dest_glyph).drop_hints (); - - if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) - Glyph (dest_glyph).set_overlaps_flag (); - - return_trace (true); - } - - void drop_hints_bytes () - { source_glyph.drop_hints_bytes (dest_start, dest_end); } - - unsigned int length () const { return dest_start.length + dest_end.length; } - /* pad to 2 to ensure 2-byte loca will be ok */ - unsigned int padding () const { return length () % 2; } - unsigned int padded_size () const { return length () + padding (); } - }; - - protected: - UnsizedArrayOf - dataZ; /* Glyphs data. */ - public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it _MIN instead. */ -}; - -struct glyf_accelerator_t : glyf::accelerator_t { - glyf_accelerator_t (hb_face_t *face) : glyf::accelerator_t (face) {} -}; - - -} /* namespace OT */ - +#include "OT/glyf/glyf.hh" #endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 5bfa2d096..8968eff4c 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -353,7 +353,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan, bool close_over_gpos, bool close_over_gdef) { - OT::glyf::accelerator_t glyf (plan->source); + OT::glyf_accelerator_t glyf (plan->source); #ifndef HB_NO_SUBSET_CFF OT::cff1::accelerator_t cff (plan->source); #endif diff --git a/src/meson.build b/src/meson.build index b7c11cdf6..55bb45ed2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -92,6 +92,7 @@ hb_base_sources = files( 'hb-ot-layout-gdef-table.hh', 'hb-ot-layout-gpos-table.hh', 'hb-ot-layout-gsub-table.hh', + 'OT/glyf/glyf.hh', 'OT/Layout/GSUB/Common.hh', 'OT/Layout/GSUB/Sequence.hh', 'OT/Layout/GSUB/SingleSubstFormat1.hh',