Skip to content

Instantly share code, notes, and snippets.

@simoncozens
Created June 7, 2021 19:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simoncozens/a36b5df8d2911685d0e64ed447c7cb1b to your computer and use it in GitHub Desktop.
Save simoncozens/a36b5df8d2911685d0e64ed447c7cb1b to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include "hb.hh"
uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = { 0,0,0,0,0};
uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)];
#include "main-font-text.hh"
#include "hb-open-type.hh"
#define HB_DEBUG 100
#include "hb-subset.hh"
#include "hb-open-file.hh"
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-hdmx-table.hh"
#include "hb-ot-head-table.hh"
#include "hb-ot-hhea-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-maxp-table.hh"
#include "hb-ot-color-sbix-table.hh"
#include "hb-ot-color-colr-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-vorg-table.hh"
#include "hb-ot-name-table.hh"
#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-repacker.hh"
DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0};
DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0};
DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0};
// static bool
// _is_table_present (hb_face_t *source, hb_tag_t tag)
// {
// hb_tag_t table_tags[32];
// unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
// while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables))
// {
// for (unsigned i = 0; i < num_tables; ++i)
// if (table_tags[i] == tag)
// return true;
// offset += num_tables;
// }
// return false;
// }
static hb_blob_t*
always_repack (const hb_serialize_context_t& c)
{
hb_vector_t<char> buf;
int buf_size = c.end - c.start;
if (unlikely (!buf.alloc (buf_size))) {
DEBUG_MSG (SUBSET, nullptr,
"Couldn't allocate %u bytes.",
buf_size);
return nullptr;
}
hb_serialize_context_t repacked ((void *) buf, buf_size);
hb_resolve_overflows (c.object_graph (), &repacked);
if (unlikely (repacked.in_error ())) {
DEBUG_MSG (SUBSET, nullptr,
"Repacker errored");
// TODO(garretrieger): refactor so we can share the resize/retry logic with the subset
// portion.
return nullptr;
}
DEBUG_MSG (SUBSET, nullptr,
"Repacker succeeded");
return repacked.copy_blob ();
}
template<typename TableType>
static
bool
_try_subset (const TableType *table,
hb_vector_t<char>* buf,
unsigned buf_size,
hb_subset_context_t* c /* OUT */)
{
c->serializer->start_serialize<TableType> ();
bool needed = table->subset (c);
if (!c->serializer->ran_out_of_room ())
{
c->serializer->end_serialize ();
return needed;
}
buf_size += (buf_size >> 1) + 32;
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.",
HB_UNTAG (c->table_tag), buf_size);
if (unlikely (!buf->alloc (buf_size)))
{
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.",
HB_UNTAG (c->table_tag), buf_size);
return needed;
}
c->serializer->reset (buf->arrayZ, buf_size);
return _try_subset (table, buf, buf_size, c);
}
template<typename TableType>
static bool
repack_and_dump (hb_subset_plan_t* plan)
{
hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
const TableType *table = source_blob->as<TableType> ();
hb_tag_t tag = TableType::tableTag;
if (!source_blob->data)
{
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
hb_blob_destroy (source_blob);
return false;
}
hb_vector_t<char> buf;
unsigned buf_size = source_blob->length;
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
if (unlikely (!buf.alloc (buf_size)))
{
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
hb_blob_destroy (source_blob);
return false;
}
hb_serialize_context_t serializer (buf.arrayZ, buf_size);
{
hb_subset_context_t c (source_blob, plan, &serializer, tag);
_try_subset (table, &buf, buf_size, &c);
}
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c before repack", HB_UNTAG (tag));
bool result = false;
hb_blob_t *dest_blob = always_repack (serializer);
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c after repack: %p", HB_UNTAG (tag), dest_blob);
if (dest_blob)
{
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c final subset table size: %u bytes.",
HB_UNTAG (tag), dest_blob->length);
result = plan->add_table (tag, dest_blob);
}
return result;
}
static bool
_add_packed_table (hb_subset_plan_t* plan, hb_tag_t tag)
{
DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag));
switch (tag)
{
case HB_OT_TAG_GDEF: return repack_and_dump<const OT::GDEF> (plan);
case HB_OT_TAG_GSUB: return repack_and_dump<const OT::GSUB> (plan);
case HB_OT_TAG_GPOS: return repack_and_dump<const OT::GPOS> (plan);
default:
hb_blob_t *source_table = hb_face_reference_table (plan->source, tag);
bool result = hb_face_builder_add_table(plan->dest, tag, source_table);
hb_blob_destroy (source_table);
return result;
}
}
struct repack_consumer_t
{
repack_consumer_t (option_parser_t *parser)
: failed (false), options (parser), font (nullptr) {}
void init (hb_buffer_t *buffer_,
const font_options_t *font_opts)
{
font = hb_font_reference (font_opts->get_font ());
}
void consume_line (const char *text,
unsigned int text_len,
const char *text_before,
const char *text_after)
{
}
hb_bool_t
write_file (const char *output_file, hb_blob_t *blob) {
unsigned int size;
const char* data = hb_blob_get_data (blob, &size);
if (!output_file)
fail (true, "No output file was specified");
FILE *fp = fopen(output_file, "wb");
if (!fp)
fail (false, "Cannot open output file `%s': %s",
g_filename_display_name (output_file), strerror (errno));
while (size) {
size_t ret = fwrite (data, 1, size, fp);
size -= ret;
data += ret;
if (size && ferror (fp))
fail (false, "Failed to write output: %s", strerror (errno));
}
fclose (fp);
return true;
}
void finish (const font_options_t *font_opts)
{
hb_face_t *face = hb_font_get_face (font);
bool success = true;
hb_tag_t table_tags[32];
unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
// We must use a subset plan in order to use the serializer at all,
// so let's make a dummy one that subsets everything...
hb_subset_input_t* input = hb_subset_input_create_or_fail();
hb_set_t *codepoints = hb_subset_input_unicode_set (input);
hb_set_t *glyphs = hb_subset_input_glyph_set (input);
unsigned num_glyphs = hb_face_get_glyph_count (face);
if (!input) {
printf("Failed to initialize input\n");
return;
}
hb_face_collect_unicodes (face, codepoints);
for (unsigned i = 0; i < num_glyphs; ++i)
{
glyphs->add(i);
}
hb_subset_plan_t *plan = hb_subset_plan_create (face, input);
while ((hb_face_get_table_tags (face, offset, &num_tables, table_tags), num_tables))
{
for (unsigned i = 0; i < num_tables; ++i)
{
hb_tag_t tag = table_tags[i];
success = _add_packed_table(plan, tag);
if (unlikely (!success)) {
printf("Failed repacking %c%c%c%c\n", HB_UNTAG(tag));
}
}
offset += num_tables;
}
// end:
hb_blob_t *result = hb_face_reference_blob (plan->dest);
failed = !hb_blob_get_length (result);
if (!failed)
write_file (options.output_file, result);
else
printf("Failed\n");
hb_blob_destroy (result);
hb_font_destroy (font);
}
public:
bool failed;
private:
output_options_t options;
hb_font_t *font;
};
int
main (int argc, char **argv)
{
main_font_text_t<repack_consumer_t, 10, 0> driver;
return driver.main (argc, argv);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment