Merge pull request #845 from googlefonts/drophints

[subset] drop hints from composites
This commit is contained in:
rsheeter 2018-02-28 13:52:29 -08:00 committed by GitHub
commit 588a4e0f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 91 additions and 23 deletions

View File

@ -402,17 +402,17 @@ struct glyf
int16_t num_contours = (int16_t) glyph_header.numberOfContours; int16_t num_contours = (int16_t) glyph_header.numberOfContours;
if (num_contours < 0) if (num_contours < 0)
{ {
CompositeGlyphHeader::Iterator *composite_it; CompositeGlyphHeader::Iterator composite_it;
if (unlikely (!CompositeGlyphHeader::get_iterator ( if (unlikely (!CompositeGlyphHeader::get_iterator (
(const char*) this->glyf_table + start_offset, (const char*) this->glyf_table + start_offset,
end_offset - start_offset, composite_it))) return false; end_offset - start_offset, &composite_it))) return false;
const CompositeGlyphHeader *last; const CompositeGlyphHeader *last;
do { do {
last = composite_it->current; last = composite_it.current;
} while (composite_it->move_to_next()); } while (composite_it.move_to_next());
if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS) if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS)
*instruction_start = start_offset + ((char *) last - (char *) glyf_table->dataX) + last->get_size(); *instruction_start = ((char *) last - (char *) glyf_table->dataX) + last->get_size();
else else
*instruction_start = end_offset; *instruction_start = end_offset;
*instruction_end = end_offset; *instruction_end = end_offset;

View File

@ -136,6 +136,20 @@ _update_components (hb_subset_plan_t * plan,
} }
} }
static bool _remove_composite_instruction_flag(char *glyf_prime, unsigned int length)
{
/* remove WE_HAVE_INSTRUCTIONS from flags in dest */
OT::glyf::CompositeGlyphHeader::Iterator composite_it;
if (unlikely (!OT::glyf::CompositeGlyphHeader::get_iterator (glyf_prime, length, &composite_it))) return false;
const OT::glyf::CompositeGlyphHeader *glyph;
do {
glyph = composite_it.current;
OT::HBUINT16 *flags = const_cast<OT::HBUINT16 *> (&glyph->flags);
flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS);
} while (composite_it.move_to_next());
return true;
}
static bool static bool
_write_glyf_and_loca_prime (hb_subset_plan_t *plan, _write_glyf_and_loca_prime (hb_subset_plan_t *plan,
const OT::glyf::accelerator_t &glyf, const OT::glyf::accelerator_t &glyf,
@ -178,9 +192,11 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan,
{ {
memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset); 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); 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 the instructions end at the end this was a composite glyph, else simple */
if (instruction_end == end_offset) if (instruction_end == end_offset)
; // TODO(rsheeter) remove WE_HAVE_INSTRUCTIONS from last flags {
if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false;
}
else else
/* zero instruction length, which is just before instruction_start */ /* zero instruction length, which is just before instruction_start */
memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2); memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);

View File

@ -121,7 +121,7 @@ test_subset_glyf_noop (void)
} }
static void static void
test_subset_glyf_strip_hints (void) test_subset_glyf_strip_hints_simple (void)
{ {
hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf"); 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_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.nohints.ttf");
@ -143,7 +143,28 @@ test_subset_glyf_strip_hints (void)
hb_face_destroy (face_ac); hb_face_destroy (face_ac);
} }
// TODO(rsheeter): test strip hints from composite static void
test_subset_glyf_strip_hints_composite (void)
{
hb_face_t *face_components = hb_subset_test_open_font ("fonts/Roboto-Regular.components.ttf");
hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Roboto-Regular.components.1fc.nohints.ttf");
hb_set_t *codepoints = hb_set_create();
hb_set_add (codepoints, 0x1fc);
hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
*hb_subset_input_drop_hints(input) = true;
hb_face_t *face_generated_subset = hb_subset_test_create_subset (face_components, input);
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'));
check_maxp_num_glyphs(face_generated_subset, 4, false);
hb_face_destroy (face_generated_subset);
hb_face_destroy (face_subset);
hb_face_destroy (face_components);
}
// TODO(grieger): test for long loca generation. // TODO(grieger): test for long loca generation.
@ -154,7 +175,8 @@ main (int argc, char **argv)
hb_test_add (test_subset_glyf_noop); hb_test_add (test_subset_glyf_noop);
hb_test_add (test_subset_glyf); hb_test_add (test_subset_glyf);
hb_test_add (test_subset_glyf_strip_hints); hb_test_add (test_subset_glyf_strip_hints_simple);
hb_test_add (test_subset_glyf_strip_hints_composite);
hb_test_add (test_subset_glyf_with_components); hb_test_add (test_subset_glyf_with_components);
return hb_test_run(); return hb_test_run();

View File

@ -8,6 +8,7 @@ SUBDIRS =
EXTRA_DIST = \ EXTRA_DIST = \
$(TESTS) \ $(TESTS) \
expected/basics \ expected/basics \
expected/full-font \
fonts \ fonts \
profiles \ profiles \
$(NULL) $(NULL)

View File

@ -1,5 +1,6 @@
TESTS = \ TESTS = \
tests/basics.tests \ tests/basics.tests \
tests/full-font.tests \
$(NULL) $(NULL)
XFAIL_TESTS = \ XFAIL_TESTS = \

Binary file not shown.

View File

@ -0,0 +1 @@
--no-hinting

View File

@ -3,6 +3,11 @@ Roboto-Regular.abc.ttf
PROFILES: PROFILES:
default.txt default.txt
drop-hints.txt
SUBSETS: SUBSETS:
abc
b b
c
ac
a

View File

@ -0,0 +1,11 @@
FONTS:
Roboto-Regular.ttf
PROFILES:
default.txt
drop-hints.txt
SUBSETS:
abc
Ǽ!A bc

View File

@ -15,12 +15,17 @@ def usage():
print "Usage: generate-expected-outputs.py <test suite file> ..." print "Usage: generate-expected-outputs.py <test suite file> ..."
def generate_expected_output(input_file, unicodes, output_path): def generate_expected_output(input_file, unicodes, profile_flags, output_path):
check_call(["fonttools", "subset", args = ["fonttools", "subset", input_file]
input_file, args.extend(profile_flags)
"--drop-tables+=DSIG,GPOS,GSUB,GDEF", args.extend(["--notdef-outline",
"--unicodes=%s" % unicodes, "--name-IDs=*",
"--output-file=%s" % output_path]) "--name-languages=*",
"--name-legacy",
"--drop-tables+=DSIG,GPOS,GSUB,GDEF",
"--unicodes=%s" % unicodes,
"--output-file=%s" % output_path])
check_call(args)
args = sys.argv[1:] args = sys.argv[1:]
@ -37,6 +42,6 @@ for path in args:
unicodes = test.unicodes() unicodes = test.unicodes()
font_name = test.get_font_name() font_name = test.get_font_name()
print "Creating subset %s/%s" % (output_directory, font_name) print "Creating subset %s/%s" % (output_directory, font_name)
generate_expected_output(test.font_path, unicodes, generate_expected_output(test.font_path, unicodes, test.get_profile_flags(),
os.path.join(output_directory, os.path.join(output_directory,
font_name)) font_name))

View File

@ -44,6 +44,7 @@ def run_test(test):
"--font-file=" + test.font_path, "--font-file=" + test.font_path,
"--output-file=" + out_file, "--output-file=" + out_file,
"--unicodes=%s" % test.unicodes()] "--unicodes=%s" % test.unicodes()]
cli_args.extend (test.get_profile_flags())
print (' '.join(cli_args)) print (' '.join(cli_args))
_, return_code = cmd(cli_args) _, return_code = cmd(cli_args)
@ -78,7 +79,7 @@ def run_ttx(file):
def strip_check_sum (ttx_string): def strip_check_sum (ttx_string):
return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]', return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
'checkSumAdjustment value="0x00000000"', 'checkSumAdjustment value="0x00000000"',
ttx_string, count=1) ttx_string, count=1)
args = sys.argv[1:] args = sys.argv[1:]

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
import io
import os import os
# A single test in a subset test suite. Identifies a font # A single test in a subset test suite. Identifies a font
@ -13,15 +14,19 @@ class Test:
def unicodes(self): def unicodes(self):
return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset)) return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset))
def get_profile_flags(self):
with io.open(self.profile_path, mode="r", encoding="utf-8") as f:
return f.read().splitlines();
def get_font_name(self): def get_font_name(self):
font_base_name = os.path.basename(self.font_path) font_base_name = os.path.basename(self.font_path)
font_base_name_parts = os.path.splitext(font_base_name) font_base_name_parts = os.path.splitext(font_base_name)
profile_name = os.path.splitext(os.path.basename(self.profile_path))[0] profile_name = os.path.splitext(os.path.basename(self.profile_path))[0]
return "%s.%s.%s%s" % (font_base_name_parts[0], return "%s.%s.%s%s" % (font_base_name_parts[0],
profile_name, profile_name,
self.unicodes(), self.unicodes(),
font_base_name_parts[1]) font_base_name_parts[1])
# A group of tests to perform on the subsetter. Each test # A group of tests to perform on the subsetter. Each test
# Identifies a font a subsetting profile, and a subset to be cut. # Identifies a font a subsetting profile, and a subset to be cut.