[subset] sketch out dropping in-glyf instructions. Sometimes yields differnet size glyphs than fonttools, possibly due to padding not being corrected
This commit is contained in:
parent
74e0c13a4a
commit
4f07437dfe
|
@ -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<GlyphHeader> (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<HBUINT16> (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
|
||||
{
|
||||
|
|
|
@ -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<hb_codepoint_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<unsigned int> *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<unsigned int> &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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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<unsigned int> 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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() */
|
||||
|
||||
|
|
Binary file not shown.
|
@ -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);
|
||||
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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'));
|
||||
|
|
Loading…
Reference in New Issue