diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 50a711154..441d4b9ec 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -275,6 +275,91 @@ struct glyf composite); } + /* based on FontTools _g_l_y_f.py::trim */ + inline bool remove_padding(unsigned int start_offset, + unsigned int *end_offset) const + { + static const int FLAG_X_SHORT = 0x02; + static const int FLAG_Y_SHORT = 0x04; + static const int FLAG_REPEAT = 0x08; + static const int FLAG_X_SAME = 0x10; + static const int FLAG_Y_SAME = 0x20; + + if (*end_offset - start_offset < GlyphHeader::static_size) + return true; + + const char *glyph = ((const char *) glyf_table) + start_offset; + const char * const glyph_end = glyph + (*end_offset - start_offset); + const GlyphHeader &glyph_header = StructAtOffset (glyph, 0); + int16_t num_contours = (int16_t) glyph_header.numberOfContours; + + if (num_contours < 0) + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + return true; + else if (num_contours > 0) + { + unsigned int glyph_len = *end_offset - start_offset; + /* simple glyph w/contours, possibly trimmable */ + glyph += GlyphHeader::static_size + 2 * num_contours; + + if (unlikely (glyph + 2 >= glyph_end)) return false; + uint16_t nCoordinates = (uint16_t) StructAtOffset(glyph - 2, 0) + 1; + uint16_t nInstructions = (uint16_t) StructAtOffset(glyph, 0); + + glyph += 2 + nInstructions; + if (unlikely (glyph + 2 >= glyph_end)) return false; + + unsigned int coordBytes = 0; + unsigned int coordsWithFlags = 0; + while (glyph < glyph_end) + { + uint8_t flag = (uint8_t) *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (glyph >= glyph_end) + { + DEBUG_MSG(SUBSET, nullptr, "Bad flag"); + return false; + } + repeat = ((uint8_t) *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; + + coordBytes += (xBytes + yBytes) * repeat; + coordsWithFlags += repeat; + if (coordsWithFlags >= nCoordinates) + break; + } + + if (coordsWithFlags != nCoordinates) + { + DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags); + return false; + } + glyph += coordBytes; + + if (glyph < glyph_end) + *end_offset -= glyph_end - glyph; + } + return true; + } + inline bool get_offsets (hb_codepoint_t glyph, unsigned int *start_offset /* OUT */, unsigned int *end_offset /* OUT */) const @@ -291,6 +376,7 @@ struct glyf else { const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataX; + *start_offset = offsets[glyph]; *end_offset = offsets[glyph + 1]; } @@ -301,6 +387,51 @@ struct glyf return true; } + inline bool get_instruction_offsets(unsigned int start_offset, + unsigned int end_offset, + unsigned int *instruction_start /* OUT */, + unsigned int *instruction_end /* OUT */) const + { + if (end_offset - start_offset < GlyphHeader::static_size) + { + *instruction_start = 0; + *instruction_end = 0; + return true; /* Empty glyph; no instructions. */ + } + const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + 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; + 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 = start_offset + ((char *) last - (char *) glyf_table->dataX) + last->get_size(); + else + *instruction_start = end_offset; + *instruction_end = end_offset; + if (unlikely (*instruction_start > *instruction_end)) + { + DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset); + return false; + } + } + else + { + unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours; + const HBUINT16 &instruction_length = StructAtOffset (glyf_table, instruction_length_offset); + *instruction_start = instruction_length_offset + 2; + *instruction_end = *instruction_start + (uint16_t) instruction_length; + } + return true; + } + inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index d29efe9de..696a74de1 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -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 */ #include "hb-open-type-private.hh" @@ -33,26 +33,49 @@ static bool _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, hb_prealloced_array_t &glyph_ids, - bool *use_short_loca, /* OUT */ - unsigned int *glyf_size, /* OUT */ - unsigned int *loca_size /* OUT */) + hb_bool_t drop_hints, + bool *use_short_loca /* OUT */, + unsigned int *glyf_size /* OUT */, + unsigned int *loca_size /* OUT */, + hb_prealloced_array_t *instruction_ranges /* OUT */) { unsigned int total = 0; - unsigned int count = 0; 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))) - end_offset = start_offset = 0; + unsigned int *instruction_start = instruction_ranges->push(); + unsigned int *instruction_end = instruction_ranges->push(); + *instruction_start = 0; + *instruction_end = 0; - total += end_offset - start_offset; - count++; + unsigned int start_offset, end_offset; + if (unlikely (!(glyf.get_offsets(next_glyph, &start_offset, &end_offset) + && glyf.remove_padding(start_offset, &end_offset)))) + { + DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph); + continue; + } + if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size) + continue; /* 0-length glyph */ + + if (drop_hints) + { + if (unlikely (!glyf.get_instruction_offsets(start_offset, end_offset, + instruction_start, instruction_end))) + { + DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph); + return false; + } + } + + total += end_offset - start_offset - (*instruction_end - *instruction_start); + /* round2 so short loca will work */ + total += total % 2; } *glyf_size = total; *use_short_loca = (total <= 131070); - *loca_size = (count + 1) + *loca_size = (glyph_ids.len + 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", @@ -118,6 +141,7 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan, const OT::glyf::accelerator_t &glyf, const char *glyf_data, bool use_short_loca, + hb_prealloced_array_t &instruction_ranges, unsigned int glyf_prime_size, char *glyf_prime_data /* OUT */, unsigned int loca_prime_size, @@ -130,27 +154,44 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan, for (unsigned int i = 0; i < glyph_ids.len; i++) { unsigned int start_offset, end_offset; - if (unlikely (!glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset))) + if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) + && glyf.remove_padding(start_offset, &end_offset)))) end_offset = start_offset = 0; + unsigned int instruction_start = instruction_ranges[i * 2]; + unsigned int instruction_end = instruction_ranges[i * 2 + 1]; - int length = end_offset - start_offset; + int length = end_offset - start_offset - (instruction_end - instruction_start); + length += length % 2; if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size) { DEBUG_MSG (SUBSET, nullptr, - "WARNING: Attempted to write an out of bounds glyph entry for gid %d", - i); + "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)", + i, length); return false; } - memcpy (glyf_prime_data_next, glyf_data + start_offset, length); + + if (instruction_start == instruction_end) + memcpy (glyf_prime_data_next, glyf_data + start_offset, length); + else + { + memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset); + memcpy (glyf_prime_data_next + instruction_start - start_offset, glyf_data + instruction_end, end_offset - instruction_end); + /* if the instructions end at the end this was a composite glyph */ + if (instruction_end == end_offset) + ; // TODO(rsheeter) remove WE_HAVE_INSTRUCTIONS from last flags + else + /* zero instruction length, which is just before instruction_start */ + memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2); + } success = success && _write_loca_entry (i, glyf_prime_data_next - glyf_prime_data, use_short_loca, loca_prime_data, loca_prime_size); - _update_components (plan, glyf_prime_data_next, end_offset - start_offset); + _update_components (plan, glyf_prime_data_next, length); glyf_prime_data_next += length; } @@ -166,7 +207,7 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan, static bool _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, const char *glyf_data, - hb_subset_plan_t *plan, + hb_subset_plan_t *plan, bool *use_short_loca, hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */) @@ -176,25 +217,33 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, unsigned int glyf_prime_size; unsigned int loca_prime_size; + hb_prealloced_array_t instruction_ranges; + instruction_ranges.init(); if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf, glyphs_to_retain, + plan->drop_hints, use_short_loca, &glyf_prime_size, - &loca_prime_size))) { + &loca_prime_size, + &instruction_ranges))) { + instruction_ranges.finish(); return false; } - char *glyf_prime_data = (char *) malloc (glyf_prime_size); - char *loca_prime_data = (char *) malloc (loca_prime_size); + char *glyf_prime_data = (char *) calloc (1, glyf_prime_size); + char *loca_prime_data = (char *) calloc (1, loca_prime_size); if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data, *use_short_loca, + instruction_ranges, glyf_prime_size, glyf_prime_data, loca_prime_size, loca_prime_data))) { free (glyf_prime_data); free (loca_prime_data); + instruction_ranges.finish(); return false; } + instruction_ranges.finish(); *glyf_prime = hb_blob_create (glyf_prime_data, glyf_prime_size, diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc index d41cff601..c4003dd3a 100644 --- a/src/hb-subset-input.cc +++ b/src/hb-subset-input.cc @@ -53,9 +53,9 @@ hb_subset_input_create_or_fail (void) * hb_subset_input_reference: (skip) * @subset_input: a subset_input. * - * * - * Return value: + * + * Return value: * * Since: 1.8.0 **/ @@ -105,3 +105,15 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input) { return subset_input->glyphs; } + +/** + * hb_subset_input_drop_hints: + * @subset_input: a subset_input. + * + * Since: 1.8.0 + **/ +HB_EXTERN hb_bool_t * +hb_subset_input_drop_hints (hb_subset_input_t *subset_input) +{ + return &subset_input->drop_hints; +} diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 27c2c7f02..f8a09efe4 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -79,6 +79,9 @@ hb_subset_plan_add_table (hb_subset_plan_t *plan, hb_tag_t tag, hb_blob_t *contents) { + hb_blob_t *source_blob = plan->source->reference_table (tag); + DEBUG_MSG(SUBSET, nullptr, "add table %c%c%c%c, dest %d bytes, source %d bytes", HB_UNTAG(tag), hb_blob_get_length (contents), hb_blob_get_length (source_blob)); + hb_blob_destroy (source_blob); return hb_subset_face_add_table(plan->dest, tag, contents); } @@ -192,6 +195,7 @@ hb_subset_plan_create (hb_face_t *face, plan->gids_to_retain_sorted.init(); plan->source = hb_face_reference (face); plan->dest = hb_subset_face_create (); + plan->drop_hints = input->drop_hints; _populate_codepoints (input->unicodes, plan->codepoints); _populate_gids_to_retain (face, diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index a74152858..d1b66b44d 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -37,6 +37,8 @@ struct hb_subset_plan_t { hb_object_header_t header; ASSERT_POD (); + hb_bool_t drop_hints; + // 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. diff --git a/src/hb-subset-private.hh b/src/hb-subset-private.hh index cf784d725..5fa725276 100644 --- a/src/hb-subset-private.hh +++ b/src/hb-subset-private.hh @@ -43,6 +43,7 @@ struct hb_subset_input_t { hb_set_t *unicodes; hb_set_t *glyphs; + hb_bool_t drop_hints; /* TODO * * features diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 77ce8c977..9ebe5d31d 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -242,11 +242,12 @@ _subset_table (hb_subset_plan_t *plan, result = _subset (plan); break; case HB_OT_TAG_head: - // SKIP head, it's handled by glyf + // TODO that won't work well if there is no glyf + DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf"); result = true; break; case HB_OT_TAG_hhea: - // SKIP hhea, it's handled by hmtx + DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx"); return true; case HB_OT_TAG_hmtx: result = _subset (plan); @@ -255,7 +256,7 @@ _subset_table (hb_subset_plan_t *plan, result = _subset (plan); break; case HB_OT_TAG_loca: - // SKIP loca, it's handle by glyf + DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf"); return true; case HB_OT_TAG_cmap: result = _subset (plan); @@ -277,9 +278,16 @@ _subset_table (hb_subset_plan_t *plan, } static bool -_should_drop_table(hb_tag_t tag) +_should_drop_table(hb_subset_plan_t *plan, hb_tag_t tag) { switch (tag) { + case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */ + case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */ + case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */ + case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */ + case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */ + case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */ + return plan->drop_hints; case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */ case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */ case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */ @@ -316,7 +324,7 @@ hb_subset (hb_face_t *source, for (unsigned int i = 0; i < count; i++) { hb_tag_t tag = table_tags[i]; - if (_should_drop_table(tag)) + if (_should_drop_table(plan, tag)) { DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag)); continue; diff --git a/src/hb-subset.h b/src/hb-subset.h index de7759b22..55ce25b0a 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -68,6 +68,8 @@ hb_subset_input_unicode_set (hb_subset_input_t *subset_input); HB_EXTERN hb_set_t * hb_subset_input_glyph_set (hb_subset_input_t *subset_input); +HB_EXTERN hb_bool_t * +hb_subset_input_drop_hints (hb_subset_input_t *subset_input); /* hb_subset() */ diff --git a/test/api/fonts/Roboto-Regular.ac.nohints.ttf b/test/api/fonts/Roboto-Regular.ac.nohints.ttf new file mode 100644 index 000000000..b73391788 Binary files /dev/null and b/test/api/fonts/Roboto-Regular.ac.nohints.ttf differ diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h index 12f7868ad..8e579a6c3 100644 --- a/test/api/hb-subset-test.h +++ b/test/api/hb-subset-test.h @@ -95,17 +95,20 @@ hb_subset_test_open_font (const char *font_path) return NULL; /* Shut up, compiler! */ } +static inline hb_subset_input_t * +hb_subset_test_create_input(const hb_set_t *codepoints) +{ + hb_subset_input_t *input = hb_subset_input_create_or_fail (); + hb_set_t * input_codepoints = hb_subset_input_unicode_set (input); + hb_set_union (input_codepoints, codepoints); + return input; +} + static inline hb_face_t * hb_subset_test_create_subset (hb_face_t *source, - const hb_set_t *codepoints) + hb_subset_input_t *input) { hb_subset_profile_t *profile = hb_subset_profile_create(); - hb_subset_input_t *input = hb_subset_input_create_or_fail (); - - hb_set_t * input_codepoints = hb_subset_input_unicode_set (input); - - hb_set_union (input_codepoints, codepoints); - hb_face_t *subset = hb_subset (source, profile, input); g_assert (subset); @@ -119,6 +122,7 @@ hb_subset_test_check (hb_face_t *expected, hb_face_t *actual, hb_tag_t table) { + fprintf(stderr, "compare %c%c%c%c\n", HB_UNTAG(table)); hb_blob_t *expected_blob = hb_face_reference_table (expected, table); hb_blob_t *actual_blob = hb_face_reference_table (actual, table); hb_test_assert_blobs_equal (expected_blob, actual_blob); diff --git a/test/api/test-subset-cmap.c b/test/api/test-subset-cmap.c index 8798475f2..07715791d 100644 --- a/test/api/test-subset-cmap.c +++ b/test/api/test-subset-cmap.c @@ -40,7 +40,7 @@ test_subset_cmap (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 97); hb_set_add (codepoints, 99); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('c','m','a','p')); @@ -59,7 +59,7 @@ test_subset_cmap_noop (void) hb_set_add (codepoints, 97); hb_set_add (codepoints, 98); hb_set_add (codepoints, 99); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('c','m','a','p')); diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c index 96e37bbcb..f5fdf32d0 100644 --- a/test/api/test-subset-glyf.c +++ b/test/api/test-subset-glyf.c @@ -31,6 +31,18 @@ /* Unit tests for hb-subset-glyf.h */ +static void check_maxp_num_glyphs (hb_face_t *face, uint16_t expected_num_glyphs) +{ + hb_blob_t *maxp_blob = hb_face_reference_table (face, HB_TAG ('m','a','x', 'p')); + + unsigned int maxp_len; + uint8_t *raw_maxp = (uint8_t *) hb_blob_get_data(maxp_blob, &maxp_len); + uint16_t num_glyphs = (raw_maxp[4] << 8) + raw_maxp[5]; + g_assert_cmpuint(expected_num_glyphs, ==, num_glyphs); + + hb_blob_destroy (maxp_blob); +} + static void test_subset_glyf (void) { @@ -40,12 +52,12 @@ test_subset_glyf (void) hb_set_t *codepoints = hb_set_create(); hb_set_add (codepoints, 97); hb_set_add (codepoints, 99); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f')); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a')); - hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('m','a','x', 'p')); + check_maxp_num_glyphs(face_abc_subset, 3); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); @@ -60,12 +72,12 @@ test_subset_glyf_with_components (void) hb_set_t *codepoints = hb_set_create(); hb_set_add (codepoints, 0x1fc); - hb_face_t *face_generated_subset = hb_subset_test_create_subset (face_components, codepoints); + hb_face_t *face_generated_subset = hb_subset_test_create_subset (face_components, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('g','l','y','f')); hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('l','o','c', 'a')); - hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('m','a','x', 'p')); + check_maxp_num_glyphs(face_generated_subset, 4); hb_face_destroy (face_generated_subset); hb_face_destroy (face_subset); @@ -81,16 +93,42 @@ test_subset_glyf_noop (void) hb_set_add (codepoints, 97); hb_set_add (codepoints, 98); hb_set_add (codepoints, 99); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','l','y','f')); hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('l','o','c', 'a')); + check_maxp_num_glyphs(face_abc_subset, 4); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); } +static void +test_subset_glyf_strip_hints (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf"); + hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.nohints.ttf"); + + hb_set_t *codepoints = hb_set_create(); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + hb_subset_input_t *input = hb_subset_test_create_input (codepoints); + *hb_subset_input_drop_hints(input) = true; + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a')); + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f')); + check_maxp_num_glyphs(face_abc_subset, 3); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +// TODO(rsheeter): test strip hints from composite + // TODO(grieger): test for long loca generation. int @@ -98,9 +136,10 @@ main (int argc, char **argv) { hb_test_init (&argc, &argv); - hb_test_add (test_subset_glyf); - hb_test_add (test_subset_glyf_with_components); hb_test_add (test_subset_glyf_noop); + hb_test_add (test_subset_glyf); + hb_test_add (test_subset_glyf_strip_hints); + hb_test_add (test_subset_glyf_with_components); return hb_test_run(); } diff --git a/test/api/test-subset-hdmx.c b/test/api/test-subset-hdmx.c index 291eb7937..5211dbc48 100644 --- a/test/api/test-subset-hdmx.c +++ b/test/api/test-subset-hdmx.c @@ -41,7 +41,7 @@ test_subset_hdmx_simple_subset (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','d','m','x')); @@ -60,7 +60,7 @@ test_subset_hdmx_noop (void) hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'b'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('h','d','m','x')); diff --git a/test/api/test-subset-hmtx.c b/test/api/test-subset-hmtx.c index 36d7d9e80..2b7645266 100644 --- a/test/api/test-subset-hmtx.c +++ b/test/api/test-subset-hmtx.c @@ -55,7 +55,7 @@ test_subset_hmtx_simple_subset (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); check_num_hmetrics(face_abc_subset, 3); /* nothing has same width */ @@ -76,7 +76,7 @@ test_subset_hmtx_monospace (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); check_num_hmetrics(face_abc_subset, 1); /* everything has same width */ @@ -97,7 +97,7 @@ test_subset_hmtx_keep_num_metrics (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); check_num_hmetrics(face_abc_subset, 3); /* c is wider */ @@ -117,7 +117,7 @@ test_subset_hmtx_decrease_num_metrics (void) hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'b'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); check_num_hmetrics(face_abc_subset, 1); /* everything left has same width */ @@ -137,7 +137,7 @@ test_subset_hmtx_noop (void) hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'b'); hb_set_add (codepoints, 'c'); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); check_num_hmetrics(face_abc_subset, 4); /* nothing has same width */ diff --git a/test/api/test-subset-os2.c b/test/api/test-subset-os2.c index e2b96f92a..e9db9bed6 100644 --- a/test/api/test-subset-os2.c +++ b/test/api/test-subset-os2.c @@ -37,7 +37,7 @@ test_subset_os2 (void) hb_set_t *codepoints = hb_set_create(); hb_set_add (codepoints, 98); - hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_b, face_abc_subset, HB_TAG ('O','S','/','2')); diff --git a/util/hb-subset.cc b/util/hb-subset.cc index ea657affe..20617554c 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -29,6 +29,7 @@ #include "main-font-text.hh" #include "hb-subset.h" +#include "hb-subset-private.hh" /* * Command line interface to the harfbuzz font subsetter. @@ -37,7 +38,7 @@ struct subset_consumer_t { subset_consumer_t (option_parser_t *parser) - : failed (false), options (parser), font (nullptr), input (nullptr) {} + : failed (false), options (parser), subset_options (parser), font (nullptr), input (nullptr) {} void init (hb_buffer_t *buffer_, const font_options_t *font_opts) @@ -89,6 +90,8 @@ struct subset_consumer_t void finish (const font_options_t *font_opts) { + input->drop_hints = subset_options.drop_hints; + hb_subset_profile_t *subset_profile = hb_subset_profile_create(); hb_face_t *face = hb_font_get_face (font); @@ -111,6 +114,7 @@ struct subset_consumer_t private: output_options_t options; + subset_options_t subset_options; hb_font_t *font; hb_subset_input_t *input; }; diff --git a/util/options.cc b/util/options.cc index d2444a499..db7115cf5 100644 --- a/util/options.cc +++ b/util/options.cc @@ -1009,3 +1009,18 @@ format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, serialize_glyphs (buffer, font, output_format, format_flags, gs); g_string_append_c (gs, '\n'); } + +void +subset_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr}, + {nullptr} + }; + parser->add_group (entries, + "subset", + "Subset options:", + "Options subsetting", + this); +} diff --git a/util/options.hh b/util/options.hh index 09d7245fe..467350a92 100644 --- a/util/options.hh +++ b/util/options.hh @@ -655,6 +655,20 @@ struct format_options_t : option_group_t hb_bool_t trace; }; +struct subset_options_t : option_group_t +{ + subset_options_t (option_parser_t *parser) + { + drop_hints = false; + + add_options (parser); + } + + void add_options (option_parser_t *parser); + + hb_bool_t drop_hints; +}; + /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ #if defined (_MSC_VER) && (_MSC_VER < 1800)