From 4f07437dfebd3dc5923f40154c6f1b7e1dce1bd4 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Wed, 21 Feb 2018 22:23:05 -0800 Subject: [PATCH] [subset] sketch out dropping in-glyf instructions. Sometimes yields differnet size glyphs than fonttools, possibly due to padding not being corrected --- src/hb-ot-glyf-table.hh | 45 +++++++++++ src/hb-subset-glyf.cc | 78 +++++++++++++++---- src/hb-subset-input.cc | 6 ++ src/hb-subset.h | 2 + test/api/fonts/Roboto-Regular.ac.nohints.ttf | Bin 0 -> 1128 bytes test/api/hb-subset-test.h | 17 ++-- test/api/test-subset-cmap.c | 4 +- test/api/test-subset-glyf.c | 32 +++++++- test/api/test-subset-hmtx.c | 10 +-- test/api/test-subset-os2.c | 2 +- 10 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 test/api/fonts/Roboto-Regular.ac.nohints.ttf diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 50a711154..667dfb52d 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -301,6 +301,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 d57b41159..867810fb7 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,42 @@ 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; + if (unlikely (!glyf.get_offsets(next_glyph, &start_offset, &end_offset))) + { + DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph); + continue; + } + unsigned int instruction_start = 0, instruction_end = 0; + 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; + } + } + *(instruction_ranges->push()) = instruction_start; + *(instruction_ranges->push()) = instruction_end; - total += end_offset - start_offset; - count++; + total += end_offset - start_offset - (instruction_end - instruction_start); + fprintf(stderr, " %d ends at %d (was %d to %d, remove %d)\n", next_glyph, total, start_offset, end_offset, instruction_end - instruction_start); } *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 +134,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, @@ -132,25 +149,44 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan, unsigned int start_offset, end_offset; if (unlikely (!glyf.get_offsets (glyph_ids[i], &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); 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) + { + fprintf(stderr, "i=%d copy %d bytes from %d to %ld\n", i, length, start_offset, glyf_prime_data_next - glyf_prime_data); + memcpy (glyf_prime_data_next, glyf_data + start_offset, length); + } + else + { + fprintf(stderr, "i=%d copy %d bytes from %d to %ld\n", i, instruction_start - start_offset, start_offset, glyf_prime_data_next - glyf_prime_data); + fprintf(stderr, "i=%d copy %d bytes from %d to %ld\n", i, end_offset - instruction_end, instruction_end, glyf_prime_data_next + instruction_start - start_offset - glyf_prime_data); + 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 + ; // TODO(rsheeter) zero instructionLength + } 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 +202,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,12 +212,17 @@ _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; } @@ -189,12 +230,15 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, char *loca_prime_data = (char *) malloc (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..69aa9d61a 100644 --- a/src/hb-subset-input.cc +++ b/src/hb-subset-input.cc @@ -105,3 +105,9 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input) { return subset_input->glyphs; } + +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.h b/src/hb-subset.h index de7759b22..1af7cba53 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 0000000000000000000000000000000000000000..b7cdffedfa0a81ccdfc29cf283ae213c6ab6c658 GIT binary patch literal 1128 zcmZuwT}V@57=F&#*_>;bmYb!fYiCaBS2kS{A`#{)qS;gwG4i<0`LpSAC(*9Vz=RTu zjKCs=x-f_o`UAc3A`yz9LN5x6Zh{JfBJ09x>;2AIBcd1H^M23y-uHQ)@BDxRkcNI} z(B!VzUx}@-0GfWfTjZ|tRBKm0O%a|XJmjvf-FL|}U zZG>&DVY%06=C%{I5H^Ood=kpgPW^8Q>%&rS1O<3Uc#g28Qwj(8hla1DmrHneq$?`N z!|)TXVf)|_jxTFI3P+HKB4D$)Lj!V#%{|N@8JD&w`OalsNa3`O8xB3 zSlZ?K^x=jBZx-sB$sQU>cbRlI0gTp}#*0>DlqS?fyCP0Xe7SE_Kc(%N?|dEmA-^1b z@I^nRA88xDI(DYF;n<{~E98)W{rk|XJ8fs5%!#v)iJOjx5tvZ}t%B%AB6JDNo*C=o z#dY4;g8%s1vTxX^SDGGu&e*{gOPqIQHRsn-YOy{_FqNK_5}Nby3k?X<8*gMBDWRB= zwhyl6#15qv|D(s8XoNxT0y%+pOzWhh=c`ro94(A8L93E8Pswn5JZ|iT1fLEupe1FD zhXvd?YY_HmrwLeypJ}L>ODo2}s)1?+1ol>;qsoyGF~#GAl^UOjk0;&I2`E@E3uZ}5 zB;)fXQnj1MRD@KTWmD>!r$5FBFS^i7DN}k8KoBPpBBpNYQX<~YzP0JKDL!mTA4x*sH;dZB AF#rGn literal 0 HcmV?d00001 diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h index 12f7868ad..8f7fe3938 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); 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..a4b4295fa 100644 --- a/test/api/test-subset-glyf.c +++ b/test/api/test-subset-glyf.c @@ -40,7 +40,7 @@ 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')); @@ -60,7 +60,7 @@ 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')); @@ -81,7 +81,7 @@ 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')); @@ -91,6 +91,31 @@ test_subset_glyf_noop (void) 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 ('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')); + + 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 @@ -101,6 +126,7 @@ main (int argc, char **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_strip_hints); return hb_test_run(); } 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'));