Merge pull request #1564 from googlefonts/retain_gids

[subset] Add --retain-gids option to the subsetter.
This commit is contained in:
Behdad Esfahbod 2019-01-30 17:17:59 -05:00 committed by GitHub
commit d983c529b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 423 additions and 231 deletions

View File

@ -57,16 +57,19 @@ struct DeviceRecord
}
unsigned int len () const
{ return this->subset_plan->glyphs.length; }
{ return this->subset_plan->num_output_glyphs (); }
const HBUINT8* operator [] (unsigned int i) const
const HBUINT8* operator [] (unsigned int new_gid) const
{
if (unlikely (i >= len ())) return nullptr;
hb_codepoint_t gid = this->subset_plan->glyphs [i];
if (unlikely (new_gid >= len ())) return nullptr;
if (gid >= sizeDeviceRecord - DeviceRecord::min_size)
hb_codepoint_t old_gid;
if (!this->subset_plan->old_gid_for_new_gid (new_gid, &old_gid))
return &Null(HBUINT8);
if (old_gid >= sizeDeviceRecord - DeviceRecord::min_size)
return nullptr;
return &(this->source_device_record->widthsZ[gid]);
return &(this->source_device_record->widthsZ[old_gid]);
}
};
@ -140,7 +143,7 @@ struct hdmx
this->version.set (source_hdmx->version);
this->numRecords.set (source_hdmx->numRecords);
this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.length));
this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->num_output_glyphs ()));
for (unsigned int i = 0; i < source_hdmx->numRecords; i++)
{
@ -156,7 +159,7 @@ struct hdmx
static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
{
return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.length);
return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->num_output_glyphs ());
}
bool subset (hb_subset_plan_t *plan) const

View File

@ -66,7 +66,7 @@ struct hmtxvmtx
bool subset_update_header (hb_subset_plan_t *plan,
unsigned int num_hmetrics) const
unsigned int num_hmetrics) const
{
hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (plan->source, H::tableTag);
hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
@ -93,75 +93,49 @@ struct hmtxvmtx
/* All the trailing glyphs with the same advance can use one LongMetric
* and just keep LSB */
hb_vector_t<hb_codepoint_t> &gids = plan->glyphs;
unsigned int num_advances = gids.length;
unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
while (num_advances > 1 &&
last_advance == _mtx.get_advance (gids[num_advances - 2]))
{
num_advances--;
}
unsigned int num_output_glyphs = plan->num_output_glyphs ();
unsigned int num_advances = _mtx.num_advances_for_subset (plan);
/* alloc the new table */
size_t dest_sz = num_advances * 4
+ (gids.length - num_advances) * 2;
+ (num_output_glyphs - num_advances) * 2;
void *dest = (void *) malloc (dest_sz);
if (unlikely (!dest))
{
return false;
}
DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.length - num_advances, (unsigned int) dest_sz);
DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes",
HB_UNTAG(T::tableTag), num_advances, num_output_glyphs - num_advances, (unsigned int) dest_sz);
const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr);
// Copy everything over
LongMetric * old_metrics = (LongMetric *) source_table;
FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
char * dest_pos = (char *) dest;
bool failed = false;
for (unsigned int i = 0; i < gids.length; i++)
for (unsigned int i = 0; i < num_output_glyphs; i++)
{
/* the last metric or the one for gids[i] */
LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]);
if (gids[i] < _mtx.num_advances)
unsigned int side_bearing = 0;
unsigned int advance = 0;
hb_codepoint_t old_gid;
if (plan->old_gid_for_new_gid (i, &old_gid))
{
/* src is a LongMetric */
if (i < num_advances)
{
/* dest is a LongMetric, copy it */
*((LongMetric *) dest_pos) = *src_metric;
}
else
{
/* dest just sb */
*((FWORD *) dest_pos) = src_metric->sb;
}
// Glyph is not an empty glyph so copy advance and side bearing
// from the input font.
side_bearing = _mtx.get_side_bearing (old_gid);
advance = _mtx.get_advance (old_gid);
}
bool has_advance = i < num_advances;
if (has_advance)
{
((LongMetric *) dest_pos)->advance.set (advance);
((LongMetric *) dest_pos)->sb.set (side_bearing);
}
else
{
if (gids[i] >= _mtx.num_metrics)
{
DEBUG_MSG(SUBSET, nullptr, "gid %d is >= number of source metrics %d",
gids[i], _mtx.num_metrics);
failed = true;
break;
}
FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances);
if (i < num_advances)
{
/* dest needs a full LongMetric */
LongMetric *metric = (LongMetric *)dest_pos;
metric->advance = src_metric->advance;
metric->sb = src_sb;
}
else
{
/* dest just needs an sb */
*((FWORD *) dest_pos) = src_sb;
}
((FWORD *) dest_pos)->set (side_bearing);
}
dest_pos += (i < num_advances ? 4 : 2);
dest_pos += (has_advance ? 4 : 2);
}
_mtx.fini ();
@ -187,7 +161,7 @@ struct hmtxvmtx
friend struct hmtxvmtx;
void init (hb_face_t *face,
unsigned int default_advance_ = 0)
unsigned int default_advance_ = 0)
{
default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
@ -280,6 +254,32 @@ struct hmtxvmtx
return advance;
}
unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const
{
unsigned int num_advances = plan->num_output_glyphs ();
unsigned int last_advance = _advance_for_new_gid (plan,
num_advances - 1);
while (num_advances > 1 &&
last_advance == _advance_for_new_gid (plan,
num_advances - 2))
{
num_advances--;
}
return num_advances;
}
private:
unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan,
hb_codepoint_t new_gid) const
{
hb_codepoint_t old_gid;
if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
return 0;
return get_advance (old_gid);
}
public:
bool has_font_extents;
int ascender;

View File

@ -1222,7 +1222,7 @@ struct ClassDefFormat1
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset;
const hb_set_t &glyphset = *c->plan->glyphset ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_vector_t<GlyphID> glyphs;
hb_vector_t<HBUINT16> klasses;
@ -1369,7 +1369,7 @@ struct ClassDefFormat2
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset;
const hb_set_t &glyphset = *c->plan->glyphset ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_vector_t<GlyphID> glyphs;
hb_vector_t<HBUINT16> klasses;

View File

@ -105,7 +105,7 @@ struct SingleSubstFormat1
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset;
const hb_set_t &glyphset = *c->plan->glyphset ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_vector_t<GlyphID> from;
hb_vector_t<GlyphID> to;
@ -202,7 +202,7 @@ struct SingleSubstFormat2
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset;
const hb_set_t &glyphset = *c->plan->glyphset ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_vector_t<GlyphID> from;
hb_vector_t<GlyphID> to;

View File

@ -105,7 +105,7 @@ struct maxp
}
maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
maxp_prime->set_num_glyphs (plan->glyphs.length);
maxp_prime->set_num_glyphs (plan->num_output_glyphs ());
if (plan->drop_hints)
drop_hint_fields (plan, maxp_prime);

View File

@ -110,21 +110,29 @@ struct VORG
/* count the number of glyphs to be included in the subset table */
hb_vector_t<VertOriginMetric> subset_metrics;
subset_metrics.init ();
unsigned int glyph = 0;
hb_codepoint_t old_glyph = HB_SET_VALUE_INVALID;
unsigned int i = 0;
while ((glyph < plan->glyphs.length) && (i < vertYOrigins.len))
while (i < vertYOrigins.len
&& plan->glyphset ()->next (&old_glyph))
{
if (plan->glyphs[glyph] > vertYOrigins[i].glyph)
i++;
else if (plan->glyphs[glyph] < vertYOrigins[i].glyph)
glyph++;
else
while (old_glyph > vertYOrigins[i].glyph)
{
VertOriginMetric *metrics = subset_metrics.push ();
metrics->glyph.set (glyph);
metrics->vertOriginY.set (vertYOrigins[i].vertOriginY);
glyph++;
i++;
if (i >= vertYOrigins.len)
break;
}
if (old_glyph == vertYOrigins[i].glyph)
{
hb_codepoint_t new_glyph;
if (plan->new_gid_for_old_gid (old_glyph, &new_glyph))
{
VertOriginMetric *metrics = subset_metrics.push ();
metrics->glyph.set (new_glyph);
metrics->vertOriginY.set (vertYOrigins[i].vertOriginY);
}
}
}

View File

@ -469,11 +469,11 @@ struct cff_subset_plan {
supp_size = 0;
supp_codes.init ();
subset_enc_num_codes = plan->glyphs.length - 1;
subset_enc_num_codes = plan->glyphs_deprecated.length - 1;
unsigned int glyph;
for (glyph = 1; glyph < plan->glyphs.length; glyph++)
for (glyph = 1; glyph < plan->glyphs_deprecated.length; glyph++)
{
hb_codepoint_t orig_glyph = plan->glyphs[glyph];
hb_codepoint_t orig_glyph = plan->glyphs_deprecated[glyph];
code = acc.glyph_to_code (orig_glyph);
if (code == CFF_UNDEF_CODE)
{
@ -526,9 +526,9 @@ struct cff_subset_plan {
subset_charset_ranges.resize (0);
unsigned int glyph;
for (glyph = 1; glyph < plan->glyphs.length; glyph++)
for (glyph = 1; glyph < plan->glyphs_deprecated.length; glyph++)
{
hb_codepoint_t orig_glyph = plan->glyphs[glyph];
hb_codepoint_t orig_glyph = plan->glyphs_deprecated[glyph];
sid = acc.glyph_to_sid (orig_glyph);
if (!acc.is_CID ())
@ -544,7 +544,7 @@ struct cff_subset_plan {
bool two_byte = subset_charset_ranges.finalize (glyph);
size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.length - 1);
size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs_deprecated.length - 1);
if (!two_byte)
size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length;
else
@ -559,7 +559,7 @@ struct cff_subset_plan {
return Charset::calculate_serialized_size (
subset_charset_format,
subset_charset_format? subset_charset_ranges.length: plan->glyphs.length);
subset_charset_format? subset_charset_ranges.length: plan->glyphs_deprecated.length);
}
bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
@ -589,19 +589,19 @@ struct cff_subset_plan {
hb_subset_plan_t *plan)
{
/* make sure notdef is first */
if ((plan->glyphs.length == 0) || (plan->glyphs[0] != 0)) return false;
if ((plan->glyphs_deprecated.length == 0) || (plan->glyphs_deprecated[0] != 0)) return false;
final_size = 0;
num_glyphs = plan->glyphs.length;
num_glyphs = plan->glyphs_deprecated.length;
orig_fdcount = acc.fdCount;
drop_hints = plan->drop_hints;
desubroutinize = plan->desubroutinize;
/* check whether the subset renumbers any glyph IDs */
gid_renum = false;
for (unsigned int glyph = 0; glyph < plan->glyphs.length; glyph++)
for (unsigned int glyph = 0; glyph < plan->glyphs_deprecated.length; glyph++)
{
if (plan->glyphs[glyph] != glyph) {
if (plan->glyphs_deprecated[glyph] != glyph) {
gid_renum = true;
break;
}
@ -644,7 +644,7 @@ struct cff_subset_plan {
/* Determine re-mapping of font index as fdmap among other info */
if (acc.fdSelect != &Null(CFF1FDSelect))
{
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs_deprecated,
orig_fdcount,
*acc.fdSelect,
subset_fdcount,
@ -681,7 +681,7 @@ struct cff_subset_plan {
{
/* Flatten global & local subrs */
subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t>
flattener(acc, plan->glyphs, plan->drop_hints);
flattener(acc, plan->glyphs_deprecated, plan->drop_hints);
if (!flattener.flatten (subset_charstrings))
return false;
@ -691,11 +691,11 @@ struct cff_subset_plan {
else
{
/* Subset subrs: collect used subroutines, leaving all unused ones behind */
if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
if (!subr_subsetter.subset (acc, plan->glyphs_deprecated, plan->drop_hints))
return false;
/* encode charstrings, global subrs, local subrs with new subroutine numbers */
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs_deprecated, subset_charstrings))
return false;
if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
@ -784,7 +784,7 @@ struct cff_subset_plan {
offsets.charStringsInfo.offSize = calcOffSize (dataSize);
if (unlikely (offsets.charStringsInfo.offSize > 4))
return false;
final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize);
final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs_deprecated.length, dataSize);
}
/* private dicts & local subrs */
@ -816,7 +816,7 @@ struct cff_subset_plan {
if (!acc.is_CID ())
offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
return ((subset_charstrings.length == plan->glyphs.length)
return ((subset_charstrings.length == plan->glyphs_deprecated.length)
&& (fontdicts_mod.length == subset_fdcount));
}
@ -1064,7 +1064,7 @@ _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc,
unsigned int cff_prime_size = cff_plan.get_final_size ();
char *cff_prime_data = (char *) calloc (1, cff_prime_size);
if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs,
if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs_deprecated,
cff_prime_size, cff_prime_data))) {
DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
free (cff_prime_data);

View File

@ -287,7 +287,7 @@ struct cff2_subset_plan {
{
/* Flatten global & local subrs */
subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
flattener(acc, plan->glyphs, plan->drop_hints);
flattener(acc, plan->glyphs_deprecated, plan->drop_hints);
if (!flattener.flatten (subset_charstrings))
return false;
@ -297,11 +297,11 @@ struct cff2_subset_plan {
else
{
/* Subset subrs: collect used subroutines, leaving all unused ones behind */
if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
if (!subr_subsetter.subset (acc, plan->glyphs_deprecated, plan->drop_hints))
return false;
/* encode charstrings, global subrs, local subrs with new subroutine numbers */
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs_deprecated, subset_charstrings))
return false;
if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
@ -352,7 +352,7 @@ struct cff2_subset_plan {
if (acc.fdSelect != &Null(CFF2FDSelect))
{
offsets.FDSelectInfo.offset = final_size;
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs_deprecated,
orig_fdcount,
*(const FDSelect *)acc.fdSelect,
subset_fdcount,
@ -385,7 +385,7 @@ struct cff2_subset_plan {
offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = subset_charstrings.total_size ();
offsets.charStringsInfo.offSize = calcOffSize (dataSize);
final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize);
final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs_deprecated.length, dataSize);
}
/* private dicts & local subrs */
@ -584,7 +584,7 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
unsigned int cff2_prime_size = cff2_plan.get_final_size ();
char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs_deprecated,
cff2_prime_size, cff2_prime_data))) {
DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
free (cff2_prime_data);

View File

@ -29,97 +29,128 @@
#include "hb-set.h"
#include "hb-subset-glyf.hh"
static bool
_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
hb_vector_t<hb_codepoint_t> &glyph_ids,
hb_bool_t drop_hints,
bool *use_short_loca /* OUT */,
unsigned int *glyf_size /* OUT */,
unsigned int *loca_size /* OUT */,
hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
struct loca_data_t
{
unsigned int total = 0;
for (unsigned int i = 0; i < glyph_ids.length; i++)
bool is_short;
void *data;
unsigned int size;
inline bool
_write_loca_entry (unsigned int id,
unsigned int offset)
{
hb_codepoint_t next_glyph = glyph_ids[i];
if (!instruction_ranges->resize (instruction_ranges->length + 2))
unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
if ((id + 1) * entry_size <= size)
{
DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
if (is_short) {
((OT::HBUINT16*) data) [id].set (offset / 2);
} else {
((OT::HBUINT32*) data) [id].set (offset);
}
return true;
}
// Offset was not written because the write is out of bounds.
DEBUG_MSG(SUBSET,
nullptr,
"WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
id,
size);
return false;
}
};
/**
* If hints are being dropped find the range which in glyf at which
* the hinting instructions are located. Add them to the instruction_ranges
* vector.
*/
static bool
_add_instructions_range (const OT::glyf::accelerator_t &glyf,
hb_codepoint_t glyph_id,
unsigned int glyph_start_offset,
unsigned int glyph_end_offset,
bool drop_hints,
hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
{
if (!instruction_ranges->resize (instruction_ranges->length + 2))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges.");
return false;
}
unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
*instruction_start = 0;
unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
*instruction_end = 0;
if (drop_hints)
{
if (unlikely (!glyf.get_instruction_offsets (glyph_start_offset, glyph_end_offset,
instruction_start, instruction_end)))
{
DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", glyph_id);
return false;
}
unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2];
*instruction_start = 0;
unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1];
*instruction_end = 0;
}
return true;
}
static bool
_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
const hb_subset_plan_t *plan,
loca_data_t *loca_data, /* OUT */
unsigned int *glyf_size /* OUT */,
hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
{
unsigned int total = 0;
hb_codepoint_t next_glyph = HB_SET_VALUE_INVALID;
while (plan->glyphset ()->next (&next_glyph))
{
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;
start_offset = end_offset = 0;
}
if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size)
bool is_zero_length = end_offset - start_offset < OT::glyf::GlyphHeader::static_size;
if (!_add_instructions_range (glyf,
next_glyph,
start_offset,
end_offset,
plan->drop_hints && !is_zero_length,
instruction_ranges))
return false;
if (is_zero_length)
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);
total += end_offset - start_offset
- ((*instruction_ranges)[instruction_ranges->length - 1]
- (*instruction_ranges)[instruction_ranges->length - 2]);
/* round2 so short loca will work */
total += total % 2;
}
*glyf_size = total;
*use_short_loca = (total <= 131070);
*loca_size = (glyph_ids.length + 1)
* (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
loca_data->is_short = (total <= 131070);
loca_data->size = (plan->num_output_glyphs () + 1)
* (loca_data->is_short ? 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");
loca_data->size,
loca_data->is_short ? "short" : "long");
return true;
}
static bool
_write_loca_entry (unsigned int id,
unsigned int offset,
bool is_short,
void *loca_prime,
unsigned int loca_size)
{
unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
if ((id + 1) * entry_size <= loca_size)
{
if (is_short) {
((OT::HBUINT16*) loca_prime) [id].set (offset / 2);
} else {
((OT::HBUINT32*) loca_prime) [id].set (offset);
}
return true;
}
// Offset was not written because the write is out of bounds.
DEBUG_MSG(SUBSET,
nullptr,
"WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
id,
loca_size);
return false;
}
static void
_update_components (hb_subset_plan_t * plan,
char * glyph_start,
unsigned int length)
_update_components (const hb_subset_plan_t *plan,
char *glyph_start,
unsigned int length)
{
OT::glyf::CompositeGlyphHeader::Iterator iterator;
if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start,
@ -153,24 +184,35 @@ static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int l
}
static bool
_write_glyf_and_loca_prime (hb_subset_plan_t *plan,
_write_glyf_and_loca_prime (const hb_subset_plan_t *plan,
const OT::glyf::accelerator_t &glyf,
const char *glyf_data,
bool use_short_loca,
hb_vector_t<unsigned int> &instruction_ranges,
hb_vector_t<unsigned int> &instruction_ranges,
unsigned int glyf_prime_size,
char *glyf_prime_data /* OUT */,
unsigned int loca_prime_size,
char *loca_prime_data /* OUT */)
loca_data_t *loca_prime /* OUT */)
{
hb_vector_t<hb_codepoint_t> &glyph_ids = plan->glyphs;
char *glyf_prime_data_next = glyf_prime_data;
bool success = true;
for (unsigned int i = 0; i < glyph_ids.length; i++)
unsigned int i = 0;
hb_codepoint_t new_gid;
for (new_gid = 0; new_gid < plan->num_output_glyphs (); new_gid++)
{
hb_codepoint_t old_gid;
if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
{
// Empty glyph, add a loca entry and carry on.
loca_prime->_write_loca_entry (new_gid,
glyf_prime_data_next - glyf_prime_data);
continue;
}
unsigned int start_offset, end_offset;
if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) &&
if (unlikely (!(glyf.get_offsets (old_gid, &start_offset, &end_offset) &&
glyf.remove_padding (start_offset, &end_offset))))
end_offset = start_offset = 0;
@ -182,9 +224,9 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan,
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 (length %d)",
i, length);
nullptr,
"WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
i, length);
return false;
}
@ -204,22 +246,20 @@ _write_glyf_and_loca_prime (hb_subset_plan_t *plan,
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);
success = success && loca_prime->_write_loca_entry (new_gid,
glyf_prime_data_next - glyf_prime_data);
_update_components (plan, glyf_prime_data_next, length);
// TODO: don't align to two bytes if using long loca.
glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca.
i++;
}
success = success && _write_loca_entry (glyph_ids.length,
glyf_prime_data_next - glyf_prime_data,
use_short_loca,
loca_prime_data,
loca_prime_size);
// loca table has n+1 entries where the last entry signifies the end location of the last
// glyph.
success = success && loca_prime->_write_loca_entry (new_gid,
glyf_prime_data_next - glyf_prime_data);
return success;
}
@ -228,52 +268,48 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf,
const char *glyf_data,
hb_subset_plan_t *plan,
bool *use_short_loca,
hb_blob_t **glyf_prime /* OUT */,
hb_blob_t **loca_prime /* OUT */)
hb_blob_t **glyf_prime_blob /* OUT */,
hb_blob_t **loca_prime_blob /* OUT */)
{
// TODO(grieger): Sanity check allocation size for the new table.
hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
loca_data_t loca_prime;
unsigned int glyf_prime_size;
unsigned int loca_prime_size;
hb_vector_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,
plan,
&loca_prime,
&glyf_prime_size,
&loca_prime_size,
&instruction_ranges))) {
instruction_ranges.fini ();
return false;
}
*use_short_loca = loca_prime.is_short;
char *glyf_prime_data = (char *) calloc (1, glyf_prime_size);
char *loca_prime_data = (char *) calloc (1, loca_prime_size);
loca_prime.data = (void *) 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))) {
&loca_prime))) {
free (glyf_prime_data);
free (loca_prime_data);
free (loca_prime.data);
instruction_ranges.fini ();
return false;
}
instruction_ranges.fini ();
*glyf_prime = hb_blob_create (glyf_prime_data,
glyf_prime_size,
HB_MEMORY_MODE_READONLY,
glyf_prime_data,
free);
*loca_prime = hb_blob_create (loca_prime_data,
loca_prime_size,
HB_MEMORY_MODE_READONLY,
loca_prime_data,
free);
*glyf_prime_blob = hb_blob_create (glyf_prime_data,
glyf_prime_size,
HB_MEMORY_MODE_READONLY,
glyf_prime_data,
free);
*loca_prime_blob = hb_blob_create ((char *) loca_prime.data,
loca_prime.size,
HB_MEMORY_MODE_READONLY,
loca_prime.data,
free);
return true;
}

View File

@ -44,7 +44,10 @@ hb_subset_input_create_or_fail ()
input->unicodes = hb_set_create ();
input->glyphs = hb_set_create ();
input->drop_hints = false;
input->drop_layout = true;
input->desubroutinize = false;
input->retain_gids = false;
return input;
}
@ -144,3 +147,27 @@ hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input)
{
return subset_input->desubroutinize;
}
/**
* hb_subset_input_set_retain_gids:
* @subset_input: a subset_input.
* @retain_gids: If true the subsetter will not renumber glyph ids.
* Since: REPLACEME
**/
HB_EXTERN void
hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
hb_bool_t retain_gids)
{
subset_input->retain_gids = retain_gids;
}
/**
* hb_subset_input_get_retain_gids:
* Returns: value of retain_gids.
* Since: REPLACEME
**/
HB_EXTERN hb_bool_t
hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input)
{
return subset_input->retain_gids;
}

View File

@ -44,6 +44,7 @@ struct hb_subset_input_t
bool drop_hints : 1;
bool drop_layout : 1;
bool desubroutinize : 1;
bool retain_gids : 1;
/* TODO
*
* features

View File

@ -156,11 +156,32 @@ _populate_gids_to_retain (hb_face_t *face,
}
static void
_create_old_gid_to_new_gid_map (const hb_vector_t<hb_codepoint_t> &glyphs,
hb_map_t *glyph_map)
_create_old_gid_to_new_gid_map (const hb_face_t *face,
bool retain_gids,
const hb_vector_t<hb_codepoint_t> &glyphs,
hb_map_t *glyph_map, /* OUT */
hb_map_t *reverse_glyph_map, /* OUT */
unsigned int *num_glyphs /* OUT */)
{
for (unsigned int i = 0; i < glyphs.length; i++) {
glyph_map->set (glyphs[i], i);
if (!retain_gids)
{
glyph_map->set (glyphs[i], i);
reverse_glyph_map->set (i, glyphs[i]);
}
else
{
glyph_map->set (glyphs[i], glyphs[i]);
reverse_glyph_map->set (glyphs[i], glyphs[i]);
}
}
if (!retain_gids || glyphs.length == 0)
{
*num_glyphs = glyphs.length;
}
else
{
*num_glyphs = face->get_num_glyphs ();
}
}
@ -184,19 +205,25 @@ hb_subset_plan_create (hb_face_t *face,
plan->drop_layout = input->drop_layout;
plan->desubroutinize = input->desubroutinize;
plan->unicodes = hb_set_create();
plan->glyphs.init();
plan->glyphs_deprecated.init();
plan->source = hb_face_reference (face);
plan->dest = hb_face_builder_create ();
plan->codepoint_to_glyph = hb_map_create();
plan->glyph_map = hb_map_create();
plan->glyphset = _populate_gids_to_retain (face,
input->unicodes,
!plan->drop_layout,
plan->unicodes,
plan->codepoint_to_glyph,
&plan->glyphs);
_create_old_gid_to_new_gid_map (plan->glyphs,
plan->glyph_map);
plan->reverse_glyph_map = hb_map_create();
plan->_glyphset = _populate_gids_to_retain (face,
input->unicodes,
!plan->drop_layout,
plan->unicodes,
plan->codepoint_to_glyph,
&plan->glyphs_deprecated);
_create_old_gid_to_new_gid_map (face,
input->retain_gids,
plan->glyphs_deprecated,
plan->glyph_map,
plan->reverse_glyph_map,
&plan->_num_output_glyphs);
return plan;
}
@ -212,12 +239,13 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
if (!hb_object_destroy (plan)) return;
hb_set_destroy (plan->unicodes);
plan->glyphs.fini ();
plan->glyphs_deprecated.fini ();
hb_face_destroy (plan->source);
hb_face_destroy (plan->dest);
hb_map_destroy (plan->codepoint_to_glyph);
hb_map_destroy (plan->glyph_map);
hb_set_destroy (plan->glyphset);
hb_map_destroy (plan->reverse_glyph_map);
hb_set_destroy (plan->_glyphset);
free (plan);
}

View File

@ -33,6 +33,7 @@
#include "hb-subset-input.hh"
#include "hb-map.hh"
#include "hb-set.hh"
struct hb_subset_plan_t
{
@ -45,18 +46,54 @@ struct hb_subset_plan_t
// For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes;
hb_vector_t<hb_codepoint_t> glyphs;
hb_set_t *glyphset;
// The glyph subset
hb_map_t *codepoint_to_glyph;
// Old -> New glyph id mapping
hb_map_t *glyph_map;
hb_map_t *reverse_glyph_map;
// Deprecated members:
hb_vector_t<hb_codepoint_t> glyphs_deprecated;
// Plan is only good for a specific source/dest so keep them with it
hb_face_t *source;
hb_face_t *dest;
bool new_gid_for_codepoint (hb_codepoint_t codepoint,
hb_codepoint_t *new_gid) const
unsigned int _num_output_glyphs;
hb_set_t *_glyphset;
public:
/*
* The set of input glyph ids which will be retained in the subset.
*/
inline const hb_set_t *
glyphset () const
{
return _glyphset;
}
/*
* The total number of output glyphs in the final subset.
*/
inline unsigned int
num_output_glyphs () const
{
return _num_output_glyphs;
}
/*
* Given an output gid , returns true if that glyph id is an empty
* glyph (ie. it's a gid that we are dropping all data for).
*/
inline bool is_empty_glyph (hb_codepoint_t gid) const
{
return !_glyphset->has (gid);
}
inline bool new_gid_for_codepoint (hb_codepoint_t codepoint,
hb_codepoint_t *new_gid) const
{
hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint);
if (old_gid == HB_MAP_VALUE_INVALID)
@ -65,8 +102,8 @@ struct hb_subset_plan_t
return new_gid_for_old_gid (old_gid, new_gid);
}
bool new_gid_for_old_gid (hb_codepoint_t old_gid,
hb_codepoint_t *new_gid) const
inline bool new_gid_for_old_gid (hb_codepoint_t old_gid,
hb_codepoint_t *new_gid) const
{
hb_codepoint_t gid = glyph_map->get (old_gid);
if (gid == HB_MAP_VALUE_INVALID)
@ -76,7 +113,18 @@ struct hb_subset_plan_t
return true;
}
bool
inline bool old_gid_for_new_gid (hb_codepoint_t new_gid,
hb_codepoint_t *old_gid) const
{
hb_codepoint_t gid = reverse_glyph_map->get (new_gid);
if (gid == HB_MAP_VALUE_INVALID)
return false;
*old_gid = gid;
return true;
}
inline bool
add_table (hb_tag_t tag,
hb_blob_t *contents)
{

View File

@ -52,7 +52,7 @@ _plan_estimate_subset_table_size (hb_subset_plan_t *plan,
unsigned int table_len)
{
unsigned int src_glyphs = plan->source->get_num_glyphs ();
unsigned int dst_glyphs = plan->glyphset->get_population ();
unsigned int dst_glyphs = plan->glyphset ()->get_population ();
if (unlikely (!src_glyphs))
return 512 + table_len;

View File

@ -72,6 +72,12 @@ hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
HB_EXTERN hb_bool_t
hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input);
HB_EXTERN void
hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
hb_bool_t retain_gids);
HB_EXTERN hb_bool_t
hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input);
/* hb_subset () */
HB_EXTERN hb_face_t *
hb_subset (hb_face_t *source, hb_subset_input_t *input);

Binary file not shown.

View File

@ -257,6 +257,31 @@ test_subset_glyf_strip_hints_invalid (void)
hb_face_destroy (face);
}
static void
test_subset_glyf_retain_gids (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.retaingids.ttf");
hb_set_t *codepoints = hb_set_create();
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 97);
hb_set_add (codepoints, 99);
hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_retain_gids (input, true);
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'));
check_maxp_num_glyphs(face_abc_subset, 4, true);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
// TODO(grieger): test for long loca generation.
int
@ -272,6 +297,7 @@ main (int argc, char **argv)
hb_test_add (test_subset_glyf_with_components);
hb_test_add (test_subset_glyf_with_gsub);
hb_test_add (test_subset_glyf_without_gsub);
hb_test_add (test_subset_glyf_retain_gids);
return hb_test_run();
}

View File

@ -0,0 +1,2 @@
--no-hinting
--retain-gids

View File

@ -0,0 +1 @@
--retain-gids

View File

@ -4,6 +4,8 @@ Roboto-Regular.abc.ttf
PROFILES:
default.txt
drop-hints.txt
drop-hints-retain-gids.txt
retain-gids.txt
SUBSETS:
abc

View File

@ -91,6 +91,7 @@ struct subset_consumer_t
{
hb_subset_input_set_drop_layout (input, !subset_options.keep_layout);
hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
hb_subset_input_set_retain_gids (input, subset_options.retain_gids);
hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
hb_face_t *face = hb_font_get_face (font);

View File

@ -977,6 +977,7 @@ subset_options_t::add_options (option_parser_t *parser)
{
{"layout", 0, 0, G_OPTION_ARG_NONE, &this->keep_layout, "Keep OpenType Layout tables", nullptr},
{"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr},
{"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr},
{"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr},
{nullptr}

View File

@ -675,6 +675,7 @@ struct subset_options_t : option_group_t
{
keep_layout = false;
drop_hints = false;
retain_gids = false;
desubroutinize = false;
add_options (parser);
@ -684,6 +685,7 @@ struct subset_options_t : option_group_t
hb_bool_t keep_layout;
hb_bool_t drop_hints;
hb_bool_t retain_gids;
hb_bool_t desubroutinize;
};