src/hb-open-file-private.hh | 12 ++ src/hb-open-type-private.hh | 7 - src/hb-ot-cmap-table.hh | 122 +++++++++++++++++++++++--- src/hb-ot-layout-common-private.hh | 4 src/hb-ot-layout-gsub-table.hh | 10 +- src/hb-private.hh | 64 ++++++++----- src/hb-subset-glyf.cc | 67 ++++++++++---- src/hb-subset-glyf.hh | 1 src/hb-subset-plan.cc | 100 +++++++++++++++------ src/hb-subset-plan.hh | 9 + src/hb-subset.cc | 173 +++++++++++++++++++++++-------------- util/hb-subset.cc | 1 12 files changed, 410 insertions(+), 160 deletions(-)
New commits: commit af274507c4f4c5a582543affa71d81a87d6d9151 Author: Behdad Esfahbod <beh...@behdad.org> Date: Sat Feb 10 13:25:49 2018 -0600 Minor diff --git a/src/hb-private.hh b/src/hb-private.hh index 4d526d9d..4a8d950d 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -425,14 +425,13 @@ struct hb_prealloced_array_t return &array[len - 1]; } - // Alloc enouch for size if size < allocated. Don't adjust len. + /* Allocate for size but don't adjust len. */ inline bool alloc(unsigned int size) { - if (likely (size <= allocated)) - { + if (likely (size <= allocated)) return true; - } - /* Need to reallocate */ + + /* Reallocate */ unsigned int new_allocated = allocated; while (size >= new_allocated) @@ -456,16 +455,14 @@ struct hb_prealloced_array_t array = new_array; allocated = new_allocated; - + return true; } inline bool resize (unsigned int size) { - if (!alloc(size)) - { + if (!alloc (size)) return false; - } len = size; return true; commit 570d523761b23a3c668d9071712d5f10944d21fc Merge: 71130a20 d18decd2 Author: Behdad Esfahbod <beh...@behdad.org> Date: Sat Feb 10 13:24:22 2018 -0600 [subset] Merge remote-tracking branch 'googlefonts/master' commit 71130a20fae7c256b0ab1aa397cc1ac2d11dd487 Author: Behdad Esfahbod <beh...@behdad.org> Date: Sat Feb 10 13:15:57 2018 -0600 Replace Supplier.advance with Supplier::operator+= diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index ab168ab8..39399052 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -151,8 +151,8 @@ typedef struct OffsetTable if (rec.length % 4) p = c->allocate_size<void> (4 - rec.length % 4); } - tags.advance (table_count); - blobs.advance (table_count); + tags += table_count; + blobs += table_count; /* TODO: update head table checkSumAdjustment. */ diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index 3a8f738b..ef49ea91 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -511,12 +511,13 @@ struct Supplier return * (const Type *) (const void *) ((const char *) head + stride * i); } - inline void advance (unsigned int count) + inline Supplier<Type> & operator += (unsigned int count) { if (unlikely (count > len)) count = len; len -= count; head = (const Type *) (const void *) ((const char *) head + stride * count); + return *this; } private: @@ -883,7 +884,7 @@ struct ArrayOf if (unlikely (!serialize (c, items_len))) return_trace (false); for (unsigned int i = 0; i < items_len; i++) array[i] = items[i]; - items.advance (items_len); + items += items_len; return_trace (true); } @@ -1006,7 +1007,7 @@ struct HeadlessArrayOf if (unlikely (!c->extend (*this))) return_trace (false); for (unsigned int i = 0; i < items_len - 1; i++) array[i] = items[i]; - items.advance (items_len - 1); + items += items_len - 1; return_trace (true); } diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh index 8bf69a37..cb66c81a 100644 --- a/src/hb-ot-layout-common-private.hh +++ b/src/hb-ot-layout-common-private.hh @@ -700,7 +700,7 @@ struct CoverageFormat1 if (unlikely (!c->extend (glyphArray))) return_trace (false); for (unsigned int i = 0; i < num_glyphs; i++) glyphArray[i] = glyphs[i]; - glyphs.advance (num_glyphs); + glyphs += num_glyphs; return_trace (true); } @@ -789,7 +789,7 @@ struct CoverageFormat2 } else { rangeRecord[range].end = glyphs[i]; } - glyphs.advance (num_glyphs); + glyphs += num_glyphs; return_trace (true); } diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh index 263e0a6e..97f1d21a 100644 --- a/src/hb-ot-layout-gsub-table.hh +++ b/src/hb-ot-layout-gsub-table.hh @@ -386,7 +386,7 @@ struct MultipleSubstFormat1 if (unlikely (!sequence[i].serialize (c, this).serialize (c, substitute_glyphs_list, substitute_len_list[i]))) return_trace (false); - substitute_len_list.advance (num_glyphs); + substitute_len_list += num_glyphs; if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); return_trace (true); } @@ -536,7 +536,7 @@ struct AlternateSubstFormat1 if (unlikely (!alternateSet[i].serialize (c, this).serialize (c, alternate_glyphs_list, alternate_len_list[i]))) return_trace (false); - alternate_len_list.advance (num_glyphs); + alternate_len_list += num_glyphs; if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false); return_trace (true); } @@ -757,8 +757,8 @@ struct LigatureSet ligatures[i], component_list, component_count_list[i]))) return_trace (false); - ligatures.advance (num_ligatures); - component_count_list.advance (num_ligatures); + ligatures += num_ligatures; + component_count_list += num_ligatures; return_trace (true); } @@ -850,7 +850,7 @@ struct LigatureSubstFormat1 component_count_list, ligature_per_first_glyph_count_list[i], component_list))) return_trace (false); - ligature_per_first_glyph_count_list.advance (num_first_glyphs); + ligature_per_first_glyph_count_list += num_first_glyphs; if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false); return_trace (true); } commit d18decd2013f24f315dbd3b15cdd80c5a734e7e9 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 18:41:21 2018 -0800 In glyf subsetting add suport for writing out a short loca table when possible. diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index a186cdf7..b0f44e2c 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -32,7 +32,8 @@ bool _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, hb_auto_array_t<unsigned int> &glyph_ids, - unsigned int *glyf_size /* OUT */, + bool *use_short_loca, /* OUT */ + unsigned int *glyf_size, /* OUT */ unsigned int *loca_size /* OUT */) { unsigned int total = 0; @@ -51,23 +52,37 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, } *glyf_size = total; - *loca_size = (count + 1) * sizeof(OT::HBUINT32); + *use_short_loca = (total <= 131070); + *loca_size = (count + 1) + * (*use_short_loca ? sizeof(OT::HBUINT16) : sizeof(OT::HBUINT32)); + + DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca", + total, + *loca_size, + *use_short_loca ? "short" : "long"); return true; } +void +_write_loca_entry (unsigned int id, unsigned int offset, bool is_short, void *loca_prime) { + if (is_short) { + ((OT::HBUINT16*) loca_prime) [id].set (offset / 2); + } else { + ((OT::HBUINT32*) loca_prime) [id].set (offset); + } +} + bool _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, const char *glyf_data, hb_auto_array_t<unsigned int> &glyph_ids, + bool use_short_loca, int glyf_prime_size, char *glyf_prime_data /* OUT */, int loca_prime_size, char *loca_prime_data /* OUT */) { - // TODO(grieger): Handle the missing character glyf and outline. - char *glyf_prime_data_next = glyf_prime_data; - OT::HBUINT32 *loca_prime = (OT::HBUINT32*) loca_prime_data; hb_codepoint_t next_glyph = -1; hb_codepoint_t new_glyph_id = 0; @@ -81,7 +96,8 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, int length = end_offset - start_offset; memcpy (glyf_prime_data_next, glyf_data + start_offset, length); - loca_prime[new_glyph_id].set(start_offset); + + _write_loca_entry (i, start_offset, use_short_loca, loca_prime_data); glyf_prime_data_next += length; new_glyph_id++; @@ -89,7 +105,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, // Add the last loca entry which doesn't correspond to a specific glyph // but identifies the end of the last glyphs data. - loca_prime[new_glyph_id].set(end_offset); + _write_loca_entry (new_glyph_id, end_offset, use_short_loca, loca_prime_data); return true; } @@ -98,18 +114,20 @@ bool _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, const char *glyf_data, hb_auto_array_t<unsigned int> &glyphs_to_retain, + bool *use_short_loca, hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */) { // TODO(grieger): Sanity check writes to make sure they are in-bounds. // TODO(grieger): Sanity check allocation size for the new table. // TODO(grieger): Don't fail on bad offsets, just dump them. - // TODO(grieger): Support short loca output. unsigned int glyf_prime_size; unsigned int loca_prime_size; + if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf, glyphs_to_retain, + use_short_loca, &glyf_prime_size, &loca_prime_size))) { return false; @@ -118,6 +136,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, char *glyf_prime_data = (char *) calloc (glyf_prime_size, 1); char *loca_prime_data = (char *) calloc (loca_prime_size, 1); if (unlikely (!_write_glyf_and_loca_prime (glyf, glyf_data, glyphs_to_retain, + *use_short_loca, glyf_prime_size, glyf_prime_data, loca_prime_size, loca_prime_data))) { free (glyf_prime_data); @@ -157,7 +176,12 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, + glyf_data, + plan->gids_to_retain_sorted, + use_short_loca, + glyf_prime, + loca_prime); glyf.fini(); *use_short_loca = false; commit 9275bd03ea427eb607dde6a8e65f78a350b88323 Author: Rod Sheeter <rshee...@google.com> Date: Fri Feb 9 17:33:34 2018 -0800 First pass at building a cmap diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index bb3eba47..daee6ca5 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -254,6 +254,8 @@ struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {}; template <typename T> struct CmapSubtableLongSegmented { + friend struct cmap; + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { int i = groups.bsearch (codepoint); @@ -269,6 +271,20 @@ struct CmapSubtableLongSegmented return_trace (c->check_struct (this) && groups.sanitize (c)); } + inline bool serialize(hb_serialize_context_t *context, + unsigned int group_count, + Supplier<CmapSubtableLongGroup> &group_supplier) + { + TRACE_SERIALIZE (this); + if (unlikely(!context->extend_min (*this))) return_trace (false); + if (unlikely(!groups.serialize(context, group_count))) return_trace (false); + for (unsigned int i = 0; i < group_count; i++) { + const CmapSubtableLongGroup &group = group_supplier[i]; + memcpy(&groups[i], &group, sizeof(group)); + } + return true; + } + protected: HBUINT16 format; /* Subtable format; set to 12. */ HBUINT16 reservedZ; /* Reserved; set to 0. */ @@ -505,15 +521,15 @@ struct cmap encodingRecord.sanitize (c, this)); } - inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const + inline void populate_groups(hb_auto_array_t<hb_codepoint_t> &codepoints, + hb_auto_array_t<CmapSubtableLongGroup> *groups) const { - hb_auto_array_t<CmapSubtableLongGroup> groups; CmapSubtableLongGroup *group = nullptr; - for (unsigned int i = 0; i < plan->codepoints.len; i++) { - hb_codepoint_t cp = plan->codepoints[i]; + for (unsigned int i = 0; i < codepoints.len; i++) { + hb_codepoint_t cp = codepoints[i]; if (!group) { - group = groups.push(); + group = groups->push(); group->startCharCode.set(cp); group->endCharCode.set(cp); group->glyphID.set(i); // index in codepoints is new gid @@ -527,13 +543,84 @@ struct cmap } DEBUG_MSG(SUBSET, nullptr, "cmap"); - for (unsigned int i = 0; i < groups.len; i++) { - CmapSubtableLongGroup& group = groups[i]; - DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID); + for (unsigned int i = 0; i < groups->len; i++) { + CmapSubtableLongGroup& group = (*groups)[i]; + DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode)); + } + } + + hb_bool_t _subset (hb_auto_array_t<CmapSubtableLongGroup> &groups, + size_t dest_sz, + void *dest) const + { + hb_serialize_context_t context(dest, dest_sz); + + OT::cmap *cmap = context.start_serialize<OT::cmap> (); + if (unlikely(!context.extend_min(*cmap))) + { + return false; + } + + cmap->version.set(0); + + if (unlikely(!cmap->encodingRecord.serialize(&context, /* numTables */ 1))) + { + return false; } + + EncodingRecord &rec = cmap->encodingRecord[0]; + rec.platformID.set (3); // Windows + rec.encodingID.set (1); // Unicode BMP + + CmapSubtable &subtable = rec.subtable.serialize(&context, &rec.subtable); + subtable.u.format.set(12); + + CmapSubtableFormat12 &format12 = subtable.u.format12; + format12.format.set(12); + format12.reservedZ.set(0); + + OT::Supplier<CmapSubtableLongGroup> group_supplier (&groups[0], groups.len, sizeof (CmapSubtableLongGroup)); + if (unlikely(!format12.serialize(&context, groups.len, group_supplier))) + { + return false; + } + + context.end_serialize (); return true; } + hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const + { + hb_auto_array_t<CmapSubtableLongGroup> groups; + + populate_groups(plan->codepoints, &groups); + + // We now know how big our blob needs to be + // TODO use APIs from the structs to get size? + size_t dest_sz = 4 // header + + 8 // 1 EncodingRecord + + 16 // Format 12 header + + 12 * groups.len; // SequentialMapGroup records + void *dest = calloc(dest_sz, 1); + if (unlikely(!dest)) { + DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %ld for cmap subset output", dest_sz); + return nullptr; + } + + if (unlikely(!_subset(groups, dest_sz, dest))) + { + free(dest); + return nullptr; + } + + // all done, write the blob into dest + return hb_blob_create((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + /* userdata */ nullptr, + free); + } + struct accelerator_t { inline void init (hb_face_t *face) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 98565c30..8e1f8198 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -108,19 +108,19 @@ hb_subset_input_destroy(hb_subset_input_t *subset_input) } template<typename TableType> -hb_bool_t -subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) +hb_blob_t * +_subset (hb_subset_plan_t *plan, hb_face_t *source) { OT::Sanitizer<TableType> sanitizer; - hb_blob_t *table_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag)); - if (unlikely(!table_blob)) { + hb_blob_t *source_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag)); + if (unlikely(!source_blob)) { DEBUG_MSG(SUBSET, nullptr, "Failed to reference table for tag %d", TableType::tableTag); - return false; + return nullptr; } - const TableType *table = OT::Sanitizer<TableType>::lock_instance (table_blob); - hb_bool_t result = table->subset(plan, source, dest); + const TableType *table = OT::Sanitizer<TableType>::lock_instance (source_blob); + hb_blob_t *result = table->subset(plan, source); - hb_blob_destroy (table_blob); + hb_blob_destroy (source_blob); hb_tag_t tag = TableType::tableTag; DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!"); @@ -242,7 +242,6 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return false; hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data; - hb_subset_face_data_t::table_entry_t *entry = data->tables.push (); if (unlikely (!entry)) return false; @@ -306,10 +305,12 @@ bool _subset_table (hb_subset_plan_t *plan, hb_face_t *source, hb_tag_t tag, - hb_blob_t *table_blob, + hb_blob_t *source_blob, hb_face_t *dest) { // TODO (grieger): Handle updating the head table (loca format + num glyphs) + DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag)); + hb_blob_t *dest_blob; switch (tag) { case HB_OT_TAG_glyf: return _subset_glyf (plan, source, dest); @@ -320,14 +321,16 @@ _subset_table (hb_subset_plan_t *plan, // SKIP loca, it's handle by glyf return true; case HB_OT_TAG_cmap: - // TODO(rsheeter): remove hb_subset_face_add_table - // once cmap subsetting works. - hb_subset_face_add_table (dest, tag, table_blob); - return subset<const OT::cmap> (plan, source, dest); + dest_blob = _subset<const OT::cmap> (plan, source); + break; default: - // Default action, copy table as is. - return hb_subset_face_add_table (dest, tag, table_blob); - } + dest_blob = source_blob; + break; + } + DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), dest_blob ? "ok" : "FAILED"); + if (unlikely(!dest_blob)) return false; + if (unlikely(!hb_subset_face_add_table (dest, tag, dest_blob))) return false; + return true; } /** commit d2170d14780ad6f8e0d17a1e011445c3bcc20871 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 17:24:16 2018 -0800 Check for failures from add table. diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 4eaf188c..98565c30 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -270,7 +270,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ HB_MEMORY_MODE_WRITABLE, head_prime, free); - hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); + has_head = has_head && hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); hb_blob_destroy (head_prime_blob); } @@ -290,8 +290,8 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) bool use_short_loca = false; // TODO(grieger): Migrate to subset function on the table like cmap. if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) { - hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); - hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + success = success && hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); + success = success && hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); success = success && _add_head_and_set_loca_version (source, use_short_loca, dest); } else { success = false; @@ -326,8 +326,7 @@ _subset_table (hb_subset_plan_t *plan, return subset<const OT::cmap> (plan, source, dest); default: // Default action, copy table as is. - hb_subset_face_add_table (dest, tag, table_blob); - return true; + return hb_subset_face_add_table (dest, tag, table_blob); } } commit 4816064c0e5464d032a55001a959a9abcef7f70e Author: Rod Sheeter <rshee...@google.com> Date: Fri Feb 9 17:14:37 2018 -0800 add missing return diff --git a/src/hb-private.hh b/src/hb-private.hh index a3d1250a..751dec60 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -448,7 +448,9 @@ struct hb_prealloced_array_t return false; array = new_array; - allocated = new_allocated; + allocated = new_allocated; + + return true; } inline bool resize (unsigned int size) commit 0089443756cdcef0182e55cf8480b96a64d31cc7 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 16:22:09 2018 -0800 Keep a second set of glyph ids in subset plan which is sorted by glyph id and always has gid 0 diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index c6e2b5db..a186cdf7 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -157,7 +157,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime); glyf.fini(); *use_short_loca = false; diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 4cb4b7c9..fb8913e8 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -30,7 +30,7 @@ #include "hb-ot-cmap-table.hh" int -hb_codepoint_t_cmp (const void *l, const void *r) { +_hb_codepoint_t_cmp (const void *l, const void *r) { return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); } @@ -40,8 +40,8 @@ hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan, hb_codepoint_t *new_gid) { // the index in old_gids is the new gid; only up to codepoints.len are valid - for (unsigned int i = 0; i < plan->codepoints.len; i++) { - if (plan->gids_to_retain[i] == old_gid) { + for (unsigned int i = 0; i < plan->gids_to_retain_sorted.len; i++) { + if (plan->gids_to_retain_sorted[i] == old_gid) { *new_gid = i; return true; } @@ -59,13 +59,14 @@ _populate_codepoints (hb_set_t *input_codepoints, hb_codepoint_t *wr = plan_codepoints.push(); *wr = cp; } - plan_codepoints.qsort (hb_codepoint_t_cmp); + plan_codepoints.qsort (_hb_codepoint_t_cmp); } void _populate_gids_to_retain (hb_face_t *face, hb_auto_array_t<hb_codepoint_t>& codepoints, - hb_auto_array_t<hb_codepoint_t>& old_gids) + hb_auto_array_t<hb_codepoint_t>& old_gids, + hb_auto_array_t<hb_codepoint_t>& old_gids_sorted) { OT::cmap::accelerator_t cmap; cmap.init (face); @@ -73,12 +74,16 @@ _populate_gids_to_retain (hb_face_t *face, hb_auto_array_t<unsigned int> bad_indices; old_gids.alloc (codepoints.len); + bool has_zero = false; for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; if (!cmap.get_nominal_glyph (codepoints[i], &gid)) { gid = -1; *(bad_indices.push ()) = i; } + if (gid == 0) { + has_zero = true; + } *(old_gids.push ()) = gid; } @@ -90,13 +95,20 @@ _populate_gids_to_retain (hb_face_t *face, old_gids.remove (i); } + // Populate a second glyph id array that is sorted by glyph id + // and is gauranteed to contain 0. + old_gids_sorted.alloc (old_gids.len + (has_zero ? 0 : 1)); + for (unsigned int i = 0; i < old_gids.len; i++) { + *(old_gids_sorted.push ()) = old_gids[i]; + } + if (!has_zero) + *(old_gids_sorted.push ()) = 0; + old_gids_sorted.qsort (_hb_codepoint_t_cmp); + for (unsigned int i = 0; i < codepoints.len; i++) { DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i); } - // TODO always keep .notdef - - // TODO(Q1) expand with glyphs that make up complex glyphs // TODO expand with glyphs reached by G* // @@ -120,7 +132,10 @@ hb_subset_plan_create (hb_face_t *face, { hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> (); _populate_codepoints (input->codepoints, plan->codepoints); - _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain); + _populate_gids_to_retain (face, + plan->codepoints, + plan->gids_to_retain, + plan->gids_to_retain_sorted); return plan; } @@ -143,5 +158,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) plan->codepoints.finish (); plan->gids_to_retain.finish (); + plan->gids_to_retain_sorted.finish (); free (plan); } diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index c7e9108c..e1c3bd3e 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -40,6 +40,7 @@ struct hb_subset_plan_t { // codepoints is sorted and aligned with gids_to_retain. hb_auto_array_t<hb_codepoint_t> codepoints; hb_auto_array_t<hb_codepoint_t> gids_to_retain; + hb_auto_array_t<hb_codepoint_t> gids_to_retain_sorted; }; typedef struct hb_subset_plan_t hb_subset_plan_t; commit 3bc81558d836e27e77bda0d6da9c71f530719579 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 16:06:33 2018 -0800 Formatting for hb-subset-plan. diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 9dbc5a09..4cb4b7c9 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -29,15 +29,16 @@ #include "hb-subset-plan.hh" #include "hb-ot-cmap-table.hh" -int hb_codepoint_t_cmp(const void *l, const void *r) { +int +hb_codepoint_t_cmp (const void *l, const void *r) { return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); } hb_bool_t -hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, - hb_codepoint_t old_gid, - hb_codepoint_t *new_gid) { - +hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan, + hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) +{ // the index in old_gids is the new gid; only up to codepoints.len are valid for (unsigned int i = 0; i < plan->codepoints.len; i++) { if (plan->gids_to_retain[i] == old_gid) { @@ -48,43 +49,45 @@ hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, return false; } -void populate_codepoints(hb_set_t *input_codepoints, - hb_auto_array_t<hb_codepoint_t>& plan_codepoints) { - plan_codepoints.alloc(hb_set_get_population(input_codepoints)); +void +_populate_codepoints (hb_set_t *input_codepoints, + hb_auto_array_t<hb_codepoint_t>& plan_codepoints) +{ + plan_codepoints.alloc (hb_set_get_population (input_codepoints)); hb_codepoint_t cp = -1; - while (hb_set_next(input_codepoints, &cp)) { + while (hb_set_next (input_codepoints, &cp)) { hb_codepoint_t *wr = plan_codepoints.push(); *wr = cp; } - plan_codepoints.qsort(hb_codepoint_t_cmp); + plan_codepoints.qsort (hb_codepoint_t_cmp); } void -populate_gids_to_retain (hb_face_t *face, - hb_auto_array_t<hb_codepoint_t>& codepoints, - hb_auto_array_t<hb_codepoint_t>& old_gids) +_populate_gids_to_retain (hb_face_t *face, + hb_auto_array_t<hb_codepoint_t>& codepoints, + hb_auto_array_t<hb_codepoint_t>& old_gids) { OT::cmap::accelerator_t cmap; cmap.init (face); hb_auto_array_t<unsigned int> bad_indices; - old_gids.alloc(codepoints.len); + old_gids.alloc (codepoints.len); for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; - if (!cmap.get_nominal_glyph(codepoints[i], &gid)) { + if (!cmap.get_nominal_glyph (codepoints[i], &gid)) { gid = -1; - *(bad_indices.push()) = i; + *(bad_indices.push ()) = i; } - *(old_gids.push()) = gid; + *(old_gids.push ()) = gid; } while (bad_indices.len > 0) { unsigned int i = bad_indices[bad_indices.len - 1]; - bad_indices.pop(); + bad_indices.pop (); DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]); - codepoints.remove(i); - old_gids.remove(i); + codepoints.remove (i); + old_gids.remove (i); } for (unsigned int i = 0; i < codepoints.len; i++) { @@ -116,8 +119,8 @@ hb_subset_plan_create (hb_face_t *face, hb_subset_input_t *input) { hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> (); - populate_codepoints(input->codepoints, plan->codepoints); - populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain); + _populate_codepoints (input->codepoints, plan->codepoints); + _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain); return plan; } @@ -138,7 +141,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) { if (!hb_object_destroy (plan)) return; - plan->codepoints.finish(); - plan->gids_to_retain.finish(); + plan->codepoints.finish (); + plan->gids_to_retain.finish (); free (plan); } commit 86aa4b3ba7cd075f01614874dae88a771b8c54fd Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 13:54:43 2018 -0800 Return empty face on hb_subset failure instead of null. Plus some minor cleanups for _add_head_and_set_loca_version diff --git a/src/hb-subset.cc b/src/hb-subset.cc index cf8ac6cc..4eaf188c 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -258,8 +258,9 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ { hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head)); const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob); + bool has_head = (head != nullptr); - if (head) { + if (has_head) { OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1); memcpy (head_prime, head, OT::head::static_size); head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); @@ -276,7 +277,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ hb_blob_destroy (head_blob); - return !head; + return has_head; } bool @@ -298,8 +299,6 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) hb_blob_destroy (loca_prime); hb_blob_destroy (glyf_prime); - _add_head_and_set_loca_version (source, use_short_loca, dest); - return success; } @@ -345,7 +344,7 @@ hb_subset (hb_face_t *source, hb_subset_profile_t *profile, hb_subset_input_t *input) { - if (unlikely (!profile || !input || !source)) return nullptr; + if (unlikely (!profile || !input || !source)) return hb_face_get_empty(); hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); @@ -368,5 +367,5 @@ hb_subset (hb_face_t *source, // TODO(grieger): Remove once basic subsetting is working + tests updated. hb_face_destroy (dest); hb_face_reference (source); - return success ? source : nullptr; + return success ? source : hb_face_get_empty(); } commit 1582eabee6017839518b821ef93a329a0a86a453 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 12:52:08 2018 -0800 Update head table with loca format selected by glyf+loca subsetting. diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index b7412fbb..c6e2b5db 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -103,10 +103,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, { // TODO(grieger): Sanity check writes to make sure they are in-bounds. // TODO(grieger): Sanity check allocation size for the new table. - // TODO(grieger): Subset loca simultaneously. // TODO(grieger): Don't fail on bad offsets, just dump them. // TODO(grieger): Support short loca output. - // TODO(grieger): Add a extra loca entry at the end. unsigned int glyf_prime_size; unsigned int loca_prime_size; @@ -150,7 +148,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, bool hb_subset_glyf_and_loca (hb_subset_plan_t *plan, hb_face_t *face, - hb_blob_t **glyf_prime /* OUT */, + bool *use_short_loca, /* OUT */ + hb_blob_t **glyf_prime, /* OUT */ hb_blob_t **loca_prime /* OUT */) { hb_blob_t *glyf_blob = OT::Sanitizer<OT::glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf)); @@ -161,7 +160,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); glyf.fini(); - // TODO(grieger): Subset loca + *use_short_loca = false; return result; } diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh index 035085f0..dbdd3410 100644 --- a/src/hb-subset-glyf.hh +++ b/src/hb-subset-glyf.hh @@ -34,6 +34,7 @@ bool hb_subset_glyf_and_loca (hb_subset_plan_t *plan, hb_face_t *face, + bool *use_short_loca, /* OUT */ hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */); diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 73812b72..cf8ac6cc 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -254,22 +254,52 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) } bool +_add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest) +{ + hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head)); + const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob); + + if (head) { + OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1); + memcpy (head_prime, head, OT::head::static_size); + head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); + + hb_blob_t *head_prime_blob = hb_blob_create ((const char*) head_prime, + OT::head::static_size, + HB_MEMORY_MODE_WRITABLE, + head_prime, + free); + hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + } + + hb_blob_destroy (head_blob); + + return !head; +} + +bool _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) { hb_blob_t *glyf_prime = nullptr; hb_blob_t *loca_prime = nullptr; bool success = true; + bool use_short_loca = false; // TODO(grieger): Migrate to subset function on the table like cmap. - if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { + if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) { hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + success = success && _add_head_and_set_loca_version (source, use_short_loca, dest); } else { success = false; } hb_blob_destroy (loca_prime); hb_blob_destroy (glyf_prime); + _add_head_and_set_loca_version (source, use_short_loca, dest); + return success; } @@ -284,8 +314,11 @@ _subset_table (hb_subset_plan_t *plan, switch (tag) { case HB_OT_TAG_glyf: return _subset_glyf (plan, source, dest); + case HB_OT_TAG_head: + // SKIP head, it's handled by glyf + return true; case HB_OT_TAG_loca: - // SKIP loca, it's handle by the glyf subsetter. + // SKIP loca, it's handle by glyf return true; case HB_OT_TAG_cmap: // TODO(rsheeter): remove hb_subset_face_add_table commit 335bbaa66f66e86d417cc123a2bf397e8b834f64 Author: Garret Rieger <grie...@google.com> Date: Fri Feb 9 10:55:15 2018 -0800 Remove uneeded code in hb-subset. diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 50bcac79..73812b72 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -316,25 +316,6 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_face_t *face = hb_subset_face_create (); - - /* Copy tables to new face. */ - { - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) - { - hb_tag_t tag = table_tags[i]; - hb_blob_t *blob = hb_face_reference_table (source, tag); - hb_subset_face_add_table (face, tag, blob); - hb_blob_destroy (blob); - } - } while (count == ARRAY_LENGTH (table_tags)); - } - hb_face_t *dest = hb_subset_face_create (); hb_tag_t table_tags[32]; unsigned int offset = 0, count; commit 1cd98d05e07498653ba60a68b6342d1a90429eba Author: Rod Sheeter <rshee...@google.com> Date: Thu Feb 8 19:39:57 2018 -0800 Create the groups for a cmap format12. Does not yet build the actual table. diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index 9d4f0eec..bb3eba47 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -193,6 +193,7 @@ struct CmapSubtableLongGroup { friend struct CmapSubtableFormat12; friend struct CmapSubtableFormat13; + friend struct cmap; int cmp (hb_codepoint_t codepoint) const { @@ -506,6 +507,30 @@ struct cmap inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const { + hb_auto_array_t<CmapSubtableLongGroup> groups; + CmapSubtableLongGroup *group = nullptr; + for (unsigned int i = 0; i < plan->codepoints.len; i++) { + hb_codepoint_t cp = plan->codepoints[i]; + if (!group) + { + group = groups.push(); + group->startCharCode.set(cp); + group->endCharCode.set(cp); + group->glyphID.set(i); // index in codepoints is new gid + } else if (cp -1 == group->endCharCode) + { + group->endCharCode.set(cp); + } else + { + group = nullptr; + } + } + + DEBUG_MSG(SUBSET, nullptr, "cmap"); + for (unsigned int i = 0; i < groups.len; i++) { + CmapSubtableLongGroup& group = groups[i]; + DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID); + } return true; } commit 59c658c8d53481990fe0efa66422353d0687474b Author: Rod Sheeter <rshee...@google.com> Date: Thu Feb 8 19:22:47 2018 -0800 capture codepoints sorted so we can use them for cmap later. one day we will have a map diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index 53733680..9d4f0eec 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -506,20 +506,6 @@ struct cmap inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const { - // TODO something useful re: memory, write to dest - size_t dest_sz = 64536; // as much as anyone would ever need - void *dest_buf = malloc(dest_sz); - OT::hb_serialize_context_t context(dest_buf, dest_sz); - - // Same version - OT::cmap new_cmap; - new_cmap.version = version; - new_cmap.encodingRecord.len.set(1); // one format 12 subtable - - // TODO we need to actually build the format 12 subtable - - // TODO: this fails - // out->extend_min(new_cmap); return true; } diff --git a/src/hb-private.hh b/src/hb-private.hh index 59d732af..a3d1250a 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -418,34 +418,44 @@ struct hb_prealloced_array_t return &array[len - 1]; } - inline bool resize (unsigned int size) + // Alloc enouch for size if size < allocated. Don't adjust len. + inline bool alloc(unsigned int size) { - if (unlikely (size > allocated)) + if (likely (size <= allocated)) { - /* Need to reallocate */ - - unsigned int new_allocated = allocated; - while (size >= new_allocated) - new_allocated += (new_allocated >> 1) + 8; - - Type *new_array = nullptr; - - if (array == static_array) { - new_array = (Type *) calloc (new_allocated, sizeof (Type)); - if (new_array) - memcpy (new_array, array, len * sizeof (Type)); - } else { - bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type)); - if (likely (!overflows)) { - new_array = (Type *) realloc (array, new_allocated * sizeof (Type)); - } + return true; + } + /* Need to reallocate */ + + unsigned int new_allocated = allocated; + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 8; + + Type *new_array = nullptr; + + if (array == static_array) { + new_array = (Type *) calloc (new_allocated, sizeof (Type)); + if (new_array) + memcpy (new_array, array, len * sizeof (Type)); + } else { + bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type)); + if (likely (!overflows)) { + new_array = (Type *) realloc (array, new_allocated * sizeof (Type)); } + } + + if (unlikely (!new_array)) + return false; - if (unlikely (!new_array)) - return false; + array = new_array; + allocated = new_allocated; + } - array = new_array; - allocated = new_allocated; + inline bool resize (unsigned int size) + { + if (!alloc(size)) + { + return false; } len = size; @@ -488,6 +498,11 @@ struct hb_prealloced_array_t return nullptr; } + inline void qsort (int (*cmp)(const void*, const void*)) + { + ::qsort (array, len, sizeof (Type), cmp); + } + inline void qsort (void) { ::qsort (array, len, sizeof (Type), Type::cmp); diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index 49c52a91..b7412fbb 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -31,14 +31,14 @@ bool _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, - hb_set_t *glyph_ids, + hb_auto_array_t<unsigned int> &glyph_ids, unsigned int *glyf_size /* OUT */, unsigned int *loca_size /* OUT */) { unsigned int total = 0; unsigned int count = 0; - hb_codepoint_t next_glyph = -1; - while (hb_set_next(glyph_ids, &next_glyph)) { + for (unsigned int i = 0; i < glyph_ids.len; i++) { + hb_codepoint_t next_glyph = glyph_ids[i]; unsigned int start_offset, end_offset; if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { *glyf_size = 0; @@ -58,7 +58,7 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, bool _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, const char *glyf_data, - const hb_set_t *glyph_ids, + hb_auto_array_t<unsigned int> &glyph_ids, int glyf_prime_size, char *glyf_prime_data /* OUT */, int loca_prime_size, @@ -73,9 +73,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, hb_codepoint_t new_glyph_id = 0; unsigned int end_offset; - while (hb_set_next(glyph_ids, &next_glyph)) { + for (unsigned int i = 0; i < glyph_ids.len; i++) { unsigned int start_offset; - if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { + if (unlikely (!glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset))) { return false; } @@ -97,7 +97,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, bool _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, const char *glyf_data, - hb_set_t *glyphs_to_retain, + hb_auto_array_t<unsigned int> &glyphs_to_retain, hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */) { @@ -158,7 +158,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->glyphs_to_retain, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); glyf.fini(); // TODO(grieger): Subset loca diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 6f889b3c..9dbc5a09 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -29,47 +29,75 @@ #include "hb-subset-plan.hh" #include "hb-ot-cmap-table.hh" +int hb_codepoint_t_cmp(const void *l, const void *r) { + return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); +} + hb_bool_t hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, hb_codepoint_t old_gid, hb_codepoint_t *new_gid) { - // TODO(Q1) lookup in map from old:new gid - // TEMPORARY: just loop over ids to retain and count up - hb_codepoint_t current = -1; - hb_codepoint_t count = 0; - while (hb_set_next(plan->glyphs_to_retain, ¤t)) { - if (old_gid == current) { - *new_gid = count; + + // the index in old_gids is the new gid; only up to codepoints.len are valid + for (unsigned int i = 0; i < plan->codepoints.len; i++) { + if (plan->gids_to_retain[i] == old_gid) { + *new_gid = i; return true; } - count++; } return false; } -hb_set_t * -glyph_ids_to_retain (hb_face_t *face, - hb_set_t *codepoints) +void populate_codepoints(hb_set_t *input_codepoints, + hb_auto_array_t<hb_codepoint_t>& plan_codepoints) { + plan_codepoints.alloc(hb_set_get_population(input_codepoints)); + hb_codepoint_t cp = -1; + while (hb_set_next(input_codepoints, &cp)) { + hb_codepoint_t *wr = plan_codepoints.push(); + *wr = cp; + } + plan_codepoints.qsort(hb_codepoint_t_cmp); +} + +void +populate_gids_to_retain (hb_face_t *face, + hb_auto_array_t<hb_codepoint_t>& codepoints, + hb_auto_array_t<hb_codepoint_t>& old_gids) { OT::cmap::accelerator_t cmap; cmap.init (face); - hb_codepoint_t cp = -1; - hb_set_t *gids = hb_set_create(); - while (hb_set_next(codepoints, &cp)) { + + hb_auto_array_t<unsigned int> bad_indices; + + old_gids.alloc(codepoints.len); + for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; - if (cmap.get_nominal_glyph(cp, &gid)) { - DEBUG_MSG(SUBSET, nullptr, "gid for U+%04X is %d", cp, gid); - hb_set_add(gids, gid); - } else { - DEBUG_MSG(SUBSET, nullptr, "Unable to resolve gid for U+%04X", cp); + if (!cmap.get_nominal_glyph(codepoints[i], &gid)) { + gid = -1; + *(bad_indices.push()) = i; } + *(old_gids.push()) = gid; + } + + while (bad_indices.len > 0) { + unsigned int i = bad_indices[bad_indices.len - 1]; + bad_indices.pop(); + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]); + codepoints.remove(i); + old_gids.remove(i); } + for (unsigned int i = 0; i < codepoints.len; i++) { + DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i); + } + + // TODO always keep .notdef + + // TODO(Q1) expand with glyphs that make up complex glyphs // TODO expand with glyphs reached by G* // cmap.fini (); - return gids; } /** @@ -88,7 +116,8 @@ hb_subset_plan_create (hb_face_t *face, hb_subset_input_t *input) { hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> (); - plan->glyphs_to_retain = glyph_ids_to_retain (face, input->codepoints); + populate_codepoints(input->codepoints, plan->codepoints); + populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain); return plan; } @@ -96,7 +125,6 @@ hb_subset_plan_t * hb_subset_plan_get_empty () { hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> (); - plan->glyphs_to_retain = hb_set_get_empty(); return plan; } @@ -110,6 +138,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) { if (!hb_object_destroy (plan)) return; - hb_set_destroy (plan->glyphs_to_retain); + plan->codepoints.finish(); + plan->gids_to_retain.finish(); free (plan); } diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index a1e4e9e9..c7e9108c 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -21,7 +21,7 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Garret Rieger + * Google Author(s): Garret Rieger, Roderick Sheeter */ #ifndef HB_SUBSET_PLAN_HH @@ -35,7 +35,11 @@ struct hb_subset_plan_t { hb_object_header_t header; ASSERT_POD (); - hb_set_t *glyphs_to_retain; + // TODO(Q1) actual map, drop this crap + // Look at me ma, I'm a poor mans map codepoint : new gid + // codepoints is sorted and aligned with gids_to_retain. + hb_auto_array_t<hb_codepoint_t> codepoints; + hb_auto_array_t<hb_codepoint_t> gids_to_retain; }; typedef struct hb_subset_plan_t hb_subset_plan_t; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index f7c215bc..50bcac79 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -122,8 +122,8 @@ subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) hb_blob_destroy (table_blob); - // TODO string not numeric tag - DEBUG_MSG(SUBSET, nullptr, "Subset %d %s", TableType::tableTag, result ? "success" : "FAILED!"); + hb_tag_t tag = TableType::tableTag; + DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!"); return result; } @@ -316,14 +316,23 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_codepoint_t old_gid = -1; - while (hb_set_next (plan->glyphs_to_retain, &old_gid)) { - hb_codepoint_t new_gid; - if (hb_subset_plan_new_gid_for_old_id (plan, old_gid, &new_gid)) { - DEBUG_MSG (SUBSET, nullptr, "Remap %d : %d", old_gid, new_gid); - } else { - DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid); - } + hb_face_t *face = hb_subset_face_create (); + + /* Copy tables to new face. */ + { + hb_tag_t table_tags[32]; + unsigned int offset = 0, count; + do { + count = ARRAY_LENGTH (table_tags); + hb_face_get_table_tags (source, offset, &count, table_tags); + for (unsigned int i = 0; i < count; i++) + { + hb_tag_t tag = table_tags[i]; + hb_blob_t *blob = hb_face_reference_table (source, tag); + hb_subset_face_add_table (face, tag, blob); + hb_blob_destroy (blob); + } + } while (count == ARRAY_LENGTH (table_tags)); } hb_face_t *dest = hb_subset_face_create (); commit 8431c38cdc05ddcddb1aa5fbb72a95446b500fd2 Author: Rod Sheeter <rshee...@google.com> Date: Thu Feb 8 19:20:58 2018 -0800 remove output noise diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 37ec7b51..db1ca115 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -59,7 +59,6 @@ struct subset_consumer_t gunichar cp = g_utf8_get_char(c); hb_codepoint_t hb_cp = cp; // TODO(Q1) is this safe? hb_set_add(codepoints, hb_cp); - g_print (" U+%04X %" G_GINT32_FORMAT "\n", cp, cp); } while ((c = g_utf8_find_next_char(c, text + text_len)) != nullptr); } commit 5cca0c07afbe9ab4b28d333f6f853063ecd75aff Author: Rod Sheeter <rshee...@google.com> Date: Thu Feb 8 19:05:46 2018 -0800 fix comment diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index d0e03ddc..e425afc3 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -150,7 +150,7 @@ typedef struct OffsetTable rec.length.set (hb_blob_get_length (blob)); rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length); rec.offset.serialize (c, this); - // take room for the tablerec + // take room for the table void *p = c->allocate_size<void> (rec.length); if (unlikely (!p)) {return false;} // copy the actual table commit a19138e668e77a0c05da2ab065c5366c8359b377 Author: Rod Sheeter <rshee...@google.com> Date: Thu Feb 8 19:03:41 2018 -0800 comment the serialization of table diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index ab168ab8..d0e03ddc 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -133,10 +133,15 @@ typedef struct OffsetTable unsigned int table_count) { TRACE_SERIALIZE (this); + // alloc 12 for the OTHeader if (unlikely (!c->extend_min (*this))) return_trace (false); + // write sfntVersion (bytes 0..3) sfnt_version.set (sfnt_tag); + // take space for numTables, searchRange, entrySelector, RangeShift + // and the TableRecords themselves if (unlikely (!tables.serialize (c, table_count))) return_trace (false); + // write OffsetTables, alloc for and write actual table blobs for (unsigned int i = 0; i < table_count; i++) { TableRecord &rec = tables.array[i]; @@ -145,9 +150,12 @@ typedef struct OffsetTable rec.length.set (hb_blob_get_length (blob)); rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length); rec.offset.serialize (c, this); + // take room for the tablerec void *p = c->allocate_size<void> (rec.length); if (unlikely (!p)) {return false;} + // copy the actual table memcpy (p, hb_blob_get_data (blob, nullptr), rec.length); + // 4-byte allignment if (rec.length % 4) p = c->allocate_size<void> (4 - rec.length % 4); } commit 5a34114f9685680d4a8cdf85a8ac90172c5620d7 Author: Garret Rieger <grie...@google.com> Date: Thu Feb 8 18:32:24 2018 -0800 Add an extra entry to the end of the loca table to identify the end of the last glyph's data. diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index d7edd750..49c52a91 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -72,8 +72,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, hb_codepoint_t next_glyph = -1; hb_codepoint_t new_glyph_id = 0; + unsigned int end_offset; while (hb_set_next(glyph_ids, &next_glyph)) { - unsigned int start_offset, end_offset; + unsigned int start_offset; if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { return false; } @@ -86,6 +87,10 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, new_glyph_id++; } + // Add the last loca entry which doesn't correspond to a specific glyph + // but identifies the end of the last glyphs data. + loca_prime[new_glyph_id].set(end_offset); + return true; } commit aac7d962120aa137385324b33a173df4f19fd80b Author: Garret Rieger <grie...@google.com> Date: Thu Feb 8 18:18:16 2018 -0800 Apply per table subsetting while building the new face in hb_subset. diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index 8221a43d..d7edd750 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -101,6 +101,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, // TODO(grieger): Subset loca simultaneously. // TODO(grieger): Don't fail on bad offsets, just dump them. // TODO(grieger): Support short loca output. + // TODO(grieger): Add a extra loca entry at the end. unsigned int glyf_prime_size; unsigned int loca_prime_size; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index a46cbd08..f7c215bc 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -253,6 +253,52 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return true; } +bool +_subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) +{ + hb_blob_t *glyf_prime = nullptr; + hb_blob_t *loca_prime = nullptr; + + bool success = true; + // TODO(grieger): Migrate to subset function on the table like cmap. + if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { + hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); + hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + } else { + success = false; + } + hb_blob_destroy (loca_prime); + hb_blob_destroy (glyf_prime); + + return success; +} + +bool +_subset_table (hb_subset_plan_t *plan, + hb_face_t *source, + hb_tag_t tag, + hb_blob_t *table_blob, + hb_face_t *dest) +{ + // TODO (grieger): Handle updating the head table (loca format + num glyphs) + switch (tag) { + case HB_OT_TAG_glyf: + return _subset_glyf (plan, source, dest); + case HB_OT_TAG_loca: + // SKIP loca, it's handle by the glyf subsetter. + return true; + case HB_OT_TAG_cmap: + // TODO(rsheeter): remove hb_subset_face_add_table + // once cmap subsetting works. + hb_subset_face_add_table (dest, tag, table_blob); + return subset<const OT::cmap> (plan, source, dest); + default: + // Default action, copy table as is. + hb_subset_face_add_table (dest, tag, table_blob); + return true; + } +} + /** * hb_subset: * @source: font face data to be subset. @@ -270,25 +316,6 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_face_t *face = hb_subset_face_create (); - - /* Copy tables to new face. */ - { - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) - { - hb_tag_t tag = table_tags[i]; - hb_blob_t *blob = hb_face_reference_table (source, tag); - hb_subset_face_add_table (face, tag, blob); - hb_blob_destroy (blob); - } - } while (count == ARRAY_LENGTH (table_tags)); - } - hb_codepoint_t old_gid = -1; while (hb_set_next (plan->glyphs_to_retain, &old_gid)) { hb_codepoint_t new_gid; @@ -298,29 +325,25 @@ hb_subset (hb_face_t *source, DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid); } } - // TODO: - // - Create initial header + table directory - // - Loop through the set of tables to be kept: - // - Perform table specific subsetting if defined. - // - copy the table into the output. - // - Fix header + table directory. + hb_face_t *dest = hb_subset_face_create (); + hb_tag_t table_tags[32]; + unsigned int offset = 0, count; bool success = true; + do { + count = ARRAY_LENGTH (table_tags); + hb_face_get_table_tags (source, offset, &count, table_tags); + for (unsigned int i = 0; i < count; i++) + { + hb_tag_t tag = table_tags[i]; + hb_blob_t *blob = hb_face_reference_table (source, tag); + success = success && _subset_table (plan, source, tag, blob, dest); + hb_blob_destroy (blob); + } + } while (count == ARRAY_LENGTH (table_tags)); - hb_face_t *dest = nullptr; // TODO allocate dest - - hb_blob_t *glyf_prime = nullptr; - hb_blob_t *loca_prime = nullptr; - if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { - // TODO: write new glyf and loca to new face. - } else { - success = false; - } - hb_blob_destroy (glyf_prime); - - success = success && subset<const OT::cmap>(plan, source, dest); - - hb_subset_plan_destroy (plan); - - return face; + // TODO(grieger): Remove once basic subsetting is working + tests updated. + hb_face_destroy (dest); + hb_face_reference (source); + return success ? source : nullptr; } commit 2f941053111d60433ab39cc70edd69c962896961 Author: Garret Rieger <grie...@google.com> Date: Thu Feb 8 15:55:12 2018 -0800 Disable subset tests on cmake for now. diff --git a/test/subset/CMakeLists.txt b/test/subset/CMakeLists.txt index 0a1e8f95..6fe377e1 100644 --- a/test/subset/CMakeLists.txt +++ b/test/subset/CMakeLists.txt @@ -2,8 +2,9 @@ if (HB_BUILD_UTILS) file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/Makefile.sources" SOURCES) extract_make_variable (TESTS ${SOURCES}) foreach (test IN ITEMS ${TESTS}) - add_test (NAME ${test} - COMMAND python run-tests.py $<TARGET_FILE:hb-subset> "data/${test}" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +# TODO(grieger): Re-enable once ttx is available in CI environments. +# add_test (NAME ${test} +# COMMAND python run-tests.py $<TARGET_FILE:hb-subset> "data/${test}" +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endforeach () endif () _______________________________________________ HarfBuzz mailing list HarfBuzz@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/harfbuzz