diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index b54c11fa7..2867d0f23 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -86,9 +86,13 @@ struct glyf { TRACE_SERIALIZE (this); - // TODO actually copy glyph - // TODO worry about instructions - // TODO capture dest locations for loca + // pad glyphs to 2-byte boundaries to permit short loca + HBUINT8 pad; + + it + | hb_apply ( [&] (hb_bytes_t glyph) { + glyph.copy(c); + if (glyph.length % 2) c->extend(pad); + }); return_trace (true); } @@ -103,32 +107,54 @@ struct glyf OT::glyf::accelerator_t glyf; glyf.init (c->plan->source); - // stream new-gids => pair of start/end offsets - // can now copy glyph from =>end - // TODO(instructions) + // stream new-gids => glyph to keep + // if dropping hints the glyph to keep doesn't have them anymore auto it = + hb_iota (c->plan->num_output_glyphs ()) | hb_map ([&] (hb_codepoint_t new_gid) { - unsigned int start_offset = 0, end_offset = 0; - hb_codepoint_t old_gid; - // should never happen, ALL old gids should be mapped - if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid)) return hb_pair (start_offset, end_offset); - - // being empty is perfectly normal - if (c->plan->is_empty_glyph (old_gid)) return hb_pair (start_offset, end_offset); + // should never happen fail, ALL old gids should be mapped + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid)) return hb_bytes_t (); + unsigned int start_offset, end_offset; if (unlikely (!(glyf.get_offsets (old_gid, &start_offset, &end_offset) && glyf.remove_padding (start_offset, &end_offset)))) { // TODO signal irreversible error - return hb_pair (start_offset, end_offset); + return hb_bytes_t (); } - return hb_pair (start_offset, end_offset); + hb_bytes_t glyph ((const char *) this, end_offset - start_offset); + + // if dropping hints, find hints region and subtract it + if (c->plan->drop_hints) { + unsigned int instruction_length; + if (!glyf.get_instruction_length (glyph, &instruction_length)) + { + // TODO signal irreversible failure + return hb_bytes_t (); + } + glyph = hb_bytes_t (&glyph, glyph.length - instruction_length); + } + + return glyph; }); glyf_prime->serialize (c->serializer, it); + // we know enough to write loca + // TODO calculate size, do we need two or four byte loca entries + unsigned int offset = 0; + HBUINT16 loca_entry; + loca *loca_prime = c->serializer->start_embed (); + + hb_enumerate (it) + | hb_apply ([&] (hb_pair_t p) { + offset += p.second.length; + loca_entry = offset / 2; + c->serializer->embed(loca_entry); + }); + // one for the road + c->serializer->embed(loca_entry); + return_trace (true); } @@ -412,45 +438,40 @@ struct glyf return true; } - bool get_instruction_offsets (unsigned int start_offset, - unsigned int end_offset, - unsigned int *instruction_start /* OUT */, - unsigned int *instruction_end /* OUT */) const + bool get_instruction_length (hb_bytes_t glyph, + unsigned int * length /* OUT */) const { - if (end_offset - start_offset < GlyphHeader::static_size) + /* Empty glyph; no instructions. */ + if (glyph.get_size() < GlyphHeader::static_size) { - *instruction_start = 0; - *instruction_end = 0; - return true; /* Empty glyph; no instructions. */ + *length = 0; + return true; } - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + unsigned int start = glyph.length; + unsigned int end = glyph.length; + const GlyphHeader &glyph_header = StructAtOffset (&glyph, 0); int16_t num_contours = (int16_t) glyph_header.numberOfContours; if (num_contours < 0) { CompositeGlyphHeader::Iterator composite_it; - if (unlikely (!CompositeGlyphHeader::get_iterator ( - (const char*) this->glyf_table + start_offset, - end_offset - start_offset, &composite_it))) return false; + if (unlikely (!CompositeGlyphHeader::get_iterator (&glyph, glyph.length, &composite_it))) return false; const CompositeGlyphHeader *last; do { last = composite_it.current; } while (composite_it.move_to_next ()); if ((uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS) - *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size (); - else - *instruction_start = end_offset; - *instruction_end = end_offset; - if (unlikely (*instruction_start > *instruction_end)) + start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size (); + if (unlikely (start > end)) { - DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset); + DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside %d byte buffer", start, glyph.length); return false; } } else { - unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours; - if (unlikely (instruction_length_offset + 2 > end_offset)) + unsigned int instruction_length_offset = GlyphHeader::static_size + 2 * num_contours; + if (unlikely (instruction_length_offset + 2 > glyph.length)) { DEBUG_MSG(SUBSET, nullptr, "Glyph size is too short, missing field instructionLength."); return false; @@ -459,15 +480,13 @@ struct glyf const HBUINT16 &instruction_length = StructAtOffset (glyf_table, instruction_length_offset); unsigned int start = instruction_length_offset + 2; unsigned int end = start + (uint16_t) instruction_length; - if (unlikely (end > end_offset)) // Out of bounds of the current glyph + if (unlikely (end > glyph.length)) // Out of bounds of the current glyph { DEBUG_MSG(SUBSET, nullptr, "The instructions array overruns the glyph's boundaries."); return false; } - - *instruction_start = start; - *instruction_end = end; } + *length = end - start; return true; }