diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index 762783f33..631723998 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -56,6 +56,13 @@ typedef struct TableRecord int cmp (Tag t) const { return t.cmp (tag); } + static int cmp (const void *pa, const void *pb) + { + const TableRecord *a = (const TableRecord *) pa; + const TableRecord *b = (const TableRecord *) pb; + return b->cmp (a->tag); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -64,7 +71,7 @@ typedef struct TableRecord Tag tag; /* 4-byte identifier. */ CheckSum checkSum; /* CheckSum for this table. */ - HBUINT32 offset; /* Offset from beginning of TrueType font + Offset32 offset; /* Offset from beginning of TrueType font * file. */ HBUINT32 length; /* Length of this table. */ public: @@ -118,6 +125,35 @@ typedef struct OffsetTable } public: + + inline bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Supplier &tags, + Supplier &blobs, + unsigned int table_count) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + sfnt_version.set (sfnt_tag); + if (unlikely (!tables.serialize (c, table_count))) return_trace (false); + for (unsigned int i = 0; i < table_count; i++) + { + TableRecord &rec = tables.array[i]; + hb_blob_t *blob = blobs[i]; + rec.tag.set (tags[0]); + rec.length.set (hb_blob_get_length (blob)); + rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length); + rec.offset.serialize (c, this); + void *p = c->allocate_size (rec.length); + if (unlikely (!p)) return false; + memcpy (p, hb_blob_get_data (blob, nullptr), rec.length); + if (rec.length % 4) + p = c->allocate_size (4 - rec.length % 4); + } + tables.qsort (); + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -249,6 +285,18 @@ struct OpenTypeFontFile } } + inline bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Supplier &tags, + Supplier &blobs, + unsigned int table_count) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (*this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count)); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index 04e4a4e1b..be3ecb499 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -499,15 +499,16 @@ struct hb_serialize_context_t template struct Supplier { - inline Supplier (const Type *array, unsigned int len_) + inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type)) { head = array; len = len_; + stride = stride_; } inline const Type operator [] (unsigned int i) const { if (unlikely (i >= len)) return Type (); - return head[i]; + return * (const Type *) ((const char *) head + stride * i); } inline void advance (unsigned int count) @@ -515,7 +516,7 @@ struct Supplier if (unlikely (count > len)) count = len; len -= count; - head += count; + head = (const Type *) ((const char *) head + stride * count); } private: @@ -523,6 +524,7 @@ struct Supplier inline Supplier& operator= (const Supplier &); /* Disallow copy */ unsigned int len; + unsigned int stride; const Type *head; }; @@ -717,6 +719,13 @@ struct Offset : Type inline bool is_null (void) const { return 0 == *this; } public: DEFINE_SIZE_STATIC (sizeof(Type)); + + inline void *serialize (hb_serialize_context_t *c, const void *base) + { + void *t = c->start_embed (); + this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + return t; + } }; typedef Offset Offset16; @@ -786,9 +795,7 @@ struct OffsetTo : Offset inline Type& serialize (hb_serialize_context_t *c, const void *base) { - Type *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ - return *t; + return * (Type *) Offset::serialize (c, base); } inline bool sanitize (hb_sanitize_context_t *c, const void *base) const @@ -928,6 +935,11 @@ struct ArrayOf return -1; } + inline void qsort (void) + { + ::qsort (array, len, sizeof (Type), Type::cmp); + } + private: inline bool sanitize_shallow (hb_sanitize_context_t *c) const { @@ -1072,6 +1084,15 @@ struct BinSearchHeader return_trace (c->check_struct (this)); } + inline void set (unsigned int v) + { + len.set (v); + assert (len == v); + entrySelectorZ.set (MAX (1u, _hb_bit_storage (v)) - 1); + searchRangeZ.set (16 * (1u << entrySelectorZ)); + rangeShiftZ.set (16 * MAX (0, (int) v - searchRangeZ)); + } + protected: HBUINT16 len; HBUINT16 searchRangeZ; diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh index a80ac720f..e6bd8ea5e 100644 --- a/src/hb-ot-map-private.hh +++ b/src/hb-ot-map-private.hh @@ -84,28 +84,28 @@ struct hb_ot_map_t inline hb_mask_t get_global_mask (void) const { return global_mask; } inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const { - const feature_map_t *map = features.bsearch (&feature_tag); + const feature_map_t *map = features.bsearch (feature_tag); if (shift) *shift = map ? map->shift : 0; return map ? map->mask : 0; } inline bool needs_fallback (hb_tag_t feature_tag) const { - const feature_map_t *map = features.bsearch (&feature_tag); + const feature_map_t *map = features.bsearch (feature_tag); return map ? map->needs_fallback : false; } inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const { - const feature_map_t *map = features.bsearch (&feature_tag); + const feature_map_t *map = features.bsearch (feature_tag); return map ? map->_1_mask : 0; } inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const { - const feature_map_t *map = features.bsearch (&feature_tag); + const feature_map_t *map = features.bsearch (feature_tag); return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX; } inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const { - const feature_map_t *map = features.bsearch (&feature_tag); + const feature_map_t *map = features.bsearch (feature_tag); return map ? map->stage[table_index] : (unsigned int) -1; } diff --git a/src/hb-private.hh b/src/hb-private.hh index 75cc38f76..118f2924b 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -380,6 +380,12 @@ _hb_unsigned_int_mul_overflows (unsigned int count, unsigned int size) return (size > 0) && (count >= ((unsigned int) -1) / size); } +static inline unsigned int +_hb_ceil_to_4 (unsigned int v) +{ + return ((v - 1) & 3) + 1; +} + /* arrays and maps */ @@ -493,34 +499,34 @@ struct hb_prealloced_array_t } template - inline Type *lsearch (T *x) + inline Type *lsearch (const T &x) { for (unsigned int i = 0; i < len; i++) - if (0 == this->array[i].cmp (x)) + if (0 == this->array[i].cmp (&x)) return &array[i]; return nullptr; } template - inline Type *bsearch (T *x) + inline Type *bsearch (const T &x) { unsigned int i; return bfind (x, &i) ? &array[i] : nullptr; } template - inline const Type *bsearch (T *x) const + inline const Type *bsearch (const T &x) const { unsigned int i; return bfind (x, &i) ? &array[i] : nullptr; } template - inline bool bfind (T *x, unsigned int *i) const + inline bool bfind (const T &x, unsigned int *i) const { int min = 0, max = (int) this->len - 1; while (min <= max) { int mid = (min + max) / 2; - int c = this->array[mid].cmp (x); + int c = this->array[mid].cmp (&x); if (c < 0) max = mid - 1; else if (c > 0) @@ -531,7 +537,7 @@ struct hb_prealloced_array_t return true; } } - if (max < 0 || (max < (int) this->len && this->array[max].cmp (x) > 0)) + if (max < 0 || (max < (int) this->len && this->array[max].cmp (&x) > 0)) max++; *i = max; return false; diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh index 54dd610ed..d71d41224 100644 --- a/src/hb-set-private.hh +++ b/src/hb-set-private.hh @@ -470,7 +470,7 @@ struct hb_set_t page_map_t map = {get_major (*codepoint), 0}; unsigned int i; - page_map.bfind (&map, &i); + page_map.bfind (map, &i); if (i < page_map.len) { if (pages[page_map[i].index].next (codepoint)) @@ -541,7 +541,7 @@ struct hb_set_t { page_map_t map = {get_major (g), pages.len}; unsigned int i; - if (!page_map.bfind (&map, &i)) + if (!page_map.bfind (map, &i)) { if (!resize (pages.len + 1)) return nullptr; @@ -555,7 +555,7 @@ struct hb_set_t inline page_t *page_for (hb_codepoint_t g) { page_map_t key = {get_major (g)}; - const page_map_t *found = page_map.bsearch (&key); + const page_map_t *found = page_map.bsearch (key); if (found) return &pages[found->index]; return nullptr; @@ -563,7 +563,7 @@ struct hb_set_t inline const page_t *page_for (hb_codepoint_t g) const { page_map_t key = {get_major (g)}; - const page_map_t *found = page_map.bsearch (&key); + const page_map_t *found = page_map.bsearch (key); if (found) return &pages[found->index]; return nullptr; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index f9119c37d..ed4394dac 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -32,6 +32,7 @@ #include "hb-subset-private.hh" #include "hb-subset-plan.hh" +#include "hb-open-file-private.hh" #include "hb-ot-glyf-table.hh" @@ -142,21 +143,59 @@ _hb_subset_face_data_destroy (void *user_data) { hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data; + data->tables.finish (); + free (data); } +static hb_blob_t * +_hb_subset_face_data_reference_blob (hb_subset_face_data_t *data) +{ + + unsigned int table_count = data->tables.len; + unsigned int face_length = table_count * 16 + 12; + + for (unsigned int i = 0; i < table_count; i++) + face_length += _hb_ceil_to_4 (hb_blob_get_length (data->tables.array[i].blob)); + + char *buf = (char *) malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + OT::hb_serialize_context_t c (buf, face_length); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2')); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + OT::Supplier tags_supplier (&data->tables[0].tag, table_count, sizeof (data->tables[0])); + OT::Supplier blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0])); + bool ret = f->serialize_single (&c, + sfnt_tag, + tags_supplier, + blobs_supplier, + table_count); + + c.end_serialize (); + + if (unlikely (!ret)) + { + free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free); +} + static hb_blob_t * _hb_subset_face_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data) { hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data; if (!tag) - { - /* TODO Compile face blob... */ - return nullptr; - } + return _hb_subset_face_data_reference_blob (data); - hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (&tag); + hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (tag); if (entry) return hb_blob_reference (entry->blob); @@ -182,7 +221,7 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data; - hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (&tag); + hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (tag); if (unlikely (!entry)) return false; diff --git a/test/api/test-subset.c b/test/api/test-subset.c index db4bbe923..866a8333c 100644 --- a/test/api/test-subset.c +++ b/test/api/test-subset.c @@ -32,7 +32,12 @@ /* Unit tests for hb-subset.h */ -static const char test_data[] = { 0, 0, 1, 0 }; +static const char test_data[] = { 0, 1, 0, 0, /* sfntVersion */ + 0, 0, /* numTables */ + 0, 0x10, /* searchRange */ + 0, 0, /* entrySelector */ + 0, 0, /* rangeShift */ + }; static void test_subset (void) @@ -51,7 +56,7 @@ test_subset (void) unsigned int output_length; const char *output_data = hb_blob_get_data(output, &output_length); - g_assert_cmpmem(test_data, 4, output_data, output_length); + g_assert_cmpmem (test_data, sizeof (test_data), output_data, output_length); hb_blob_destroy(output); hb_subset_input_destroy(input);