parent 777ba47b50
author ckitagawa <ckitagawa@chromium.org> 1579631743 -0500 committer ckitagawa <ckitagawa@chromium.org> 1580506176 -0500 [subset] Add CBLC support
This commit is contained in:
parent
777ba47b50
commit
e128f80278
|
@ -63,6 +63,7 @@ HB_BASE_sources = \
|
|||
hb-ot-cff2-table.cc \
|
||||
hb-ot-cff2-table.hh \
|
||||
hb-ot-cmap-table.hh \
|
||||
hb-ot-color-cbdt-table.cc \
|
||||
hb-ot-color-cbdt-table.hh \
|
||||
hb-ot-color-colr-table.hh \
|
||||
hb-ot-color-cpal-table.hh \
|
||||
|
@ -253,6 +254,7 @@ HB_SUBSET_sources = \
|
|||
hb-number.hh \
|
||||
hb-ot-cff1-table.cc \
|
||||
hb-ot-cff2-table.cc \
|
||||
hb-ot-color-cbdt-table.cc \
|
||||
hb-static.cc \
|
||||
hb-subset-cff-common.cc \
|
||||
hb-subset-cff-common.hh \
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "hb-number.cc"
|
||||
#include "hb-ot-cff1-table.cc"
|
||||
#include "hb-ot-cff2-table.cc"
|
||||
#include "hb-ot-color-cbdt-table.cc"
|
||||
#include "hb-ot-color.cc"
|
||||
#include "hb-ot-face.cc"
|
||||
#include "hb-ot-font.cc"
|
||||
|
|
|
@ -1198,10 +1198,9 @@ struct cmap
|
|||
})
|
||||
;
|
||||
|
||||
|
||||
if (unlikely (!encodingrec_iter.len ())) return_trace (false);
|
||||
|
||||
const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr;
|
||||
const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *unicode_uvs = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr;
|
||||
bool has_format12 = false;
|
||||
|
||||
for (const EncodingRecord& _ : encodingrec_iter)
|
||||
|
@ -1212,11 +1211,12 @@ struct cmap
|
|||
const EncodingRecord *table = hb_addressof (_);
|
||||
if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table;
|
||||
else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table;
|
||||
else if (_.platformID == 0 && _.encodingID == 5) unicode_uvs = table;
|
||||
else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table;
|
||||
else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table;
|
||||
}
|
||||
|
||||
if (unlikely (!unicode_bmp && !ms_bmp)) return_trace (false);
|
||||
if (unlikely (!unicode_bmp && !ms_bmp && !unicode_uvs)) return_trace (false);
|
||||
if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);
|
||||
|
||||
auto it =
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright © 2020 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Calder Kitagawa
|
||||
*/
|
||||
|
||||
#include "hb-open-type.hh"
|
||||
|
||||
#include "hb-ot-color-cbdt-table.hh"
|
||||
|
||||
namespace OT {
|
||||
|
||||
namespace CBDT_internal {
|
||||
|
||||
bool copy_data_to_cbdt (hb_vector_t<char> *cbdt_prime,
|
||||
const void *data,
|
||||
unsigned int length)
|
||||
{
|
||||
unsigned int new_len = cbdt_prime->length + length;
|
||||
if (unlikely (!cbdt_prime->alloc(new_len))) return false;
|
||||
memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length);
|
||||
cbdt_prime->length = new_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CBLC::subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
auto* cblc_prime = c->serializer->start_embed<CBLC> ();
|
||||
|
||||
// Use a vector as a secondary buffer as the tables need to be built in parallel.
|
||||
hb_vector_t<char> cbdt_prime;
|
||||
|
||||
if (unlikely (!cblc_prime)) return_trace (false);
|
||||
if (unlikely (!c->serializer->extend_min(cblc_prime))) return_trace (false);
|
||||
cblc_prime->version = version;
|
||||
|
||||
hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table<CBDT> (c->plan->source);
|
||||
unsigned int cbdt_length;
|
||||
CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length);
|
||||
if (unlikely (cbdt_length < CBDT::min_size)) return_trace (false);
|
||||
CBDT_internal::copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size);
|
||||
|
||||
for (const BitmapSizeTable& table : + sizeTables.iter ())
|
||||
subset_size_table (c, table, (const char *) cbdt, cblc_prime, &cbdt_prime);
|
||||
|
||||
hb_blob_destroy (cbdt_blob);
|
||||
|
||||
return_trace (CBLC::sink_cbdt (c, &cbdt_prime));
|
||||
}
|
||||
|
||||
} /* namespace OT */
|
|
@ -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): Seigo Nonaka
|
||||
* Google Author(s): Seigo Nonaka, Calder Kitagawa
|
||||
*/
|
||||
|
||||
#ifndef HB_OT_COLOR_CBDT_TABLE_HH
|
||||
|
@ -43,6 +43,31 @@
|
|||
|
||||
namespace OT {
|
||||
|
||||
namespace CBDT_internal {
|
||||
|
||||
// Helper function internal to CBDT.
|
||||
HB_INTERNAL bool copy_data_to_cbdt (hb_vector_t<char> *cbdt_prime,
|
||||
const void *data,
|
||||
unsigned int length);
|
||||
|
||||
}
|
||||
|
||||
struct cblc_bitmap_size_subset_context_t
|
||||
{
|
||||
const char *cbdt;
|
||||
hb_vector_t<char> *cbdt_prime;
|
||||
unsigned int size; /* INOUT
|
||||
* Input: old size of IndexSubtable
|
||||
* Output: new size of IndexSubtable
|
||||
*/
|
||||
unsigned int num_tables; /* INOUT
|
||||
* Input: old number of subtables.
|
||||
* Output: new number of subtables.
|
||||
*/
|
||||
hb_codepoint_t start_glyph; /* OUT */
|
||||
hb_codepoint_t end_glyph; /* OUT */
|
||||
};
|
||||
|
||||
struct SmallGlyphMetrics
|
||||
{
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
|
@ -143,6 +168,18 @@ struct IndexSubtableFormat1Or3
|
|||
return true;
|
||||
}
|
||||
|
||||
bool add_offset (hb_serialize_context_t *c,
|
||||
unsigned int offset,
|
||||
unsigned int *size /* OUT (accumulated) */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
Offset<OffsetType> embedded_offset;
|
||||
embedded_offset = offset;
|
||||
*size += sizeof(OffsetType);
|
||||
auto* o = c->embed (embedded_offset);
|
||||
return_trace ((bool) o);
|
||||
}
|
||||
|
||||
IndexSubtableHeader header;
|
||||
UnsizedArrayOf<Offset<OffsetType>>
|
||||
offsetArrayZ;
|
||||
|
@ -166,6 +203,99 @@ struct IndexSubtable
|
|||
}
|
||||
}
|
||||
|
||||
bool finish_subtable (hb_serialize_context_t *c,
|
||||
unsigned int cbdt_prime_len,
|
||||
unsigned int num_glyphs,
|
||||
unsigned int *size /* OUT (accumulated) */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset;
|
||||
switch (u.header.indexFormat) {
|
||||
case 1: return_trace (u.format1.add_offset (c, local_offset, size));
|
||||
case 3: {
|
||||
if (!u.format3.add_offset (c, local_offset, size))
|
||||
return_trace (false);
|
||||
if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed.
|
||||
return_trace (u.format3.add_offset (c, 0, size));
|
||||
return_trace(true);
|
||||
}
|
||||
// TODO: implement 2, 4, 5.
|
||||
case 2: case 4: // No-op.
|
||||
case 5: // Pad to 32-bit aligned.
|
||||
default: return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
bool fill_missing_glyphs (hb_serialize_context_t *c,
|
||||
unsigned int cbdt_prime_len,
|
||||
unsigned int num_missing,
|
||||
unsigned int *size /* OUT (accumulated) */,
|
||||
unsigned int *num_glyphs /* OUT (accumulated) */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset;
|
||||
switch (u.header.indexFormat) {
|
||||
case 1: {
|
||||
for (unsigned int i = 0; i < num_missing; i++)
|
||||
{
|
||||
if (unlikely (!u.format1.add_offset (c, local_offset, size)))
|
||||
return_trace (false);
|
||||
*num_glyphs += 1;
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
case 3: {
|
||||
for (unsigned int i = 0; i < num_missing; i++)
|
||||
{
|
||||
if (unlikely (!u.format3.add_offset (c, local_offset, size)))
|
||||
return_trace (false);
|
||||
*num_glyphs += 1;
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
// TODO: implement 2, 4, 5.
|
||||
case 2: // Add empty space in cbdt_prime?.
|
||||
case 4: case 5: // No-op as sparse is supported.
|
||||
default: return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
bool copy_glyph_at_idx (hb_serialize_context_t *c,
|
||||
unsigned int idx,
|
||||
const char *cbdt,
|
||||
hb_vector_t<char> *cbdt_prime /* INOUT */,
|
||||
IndexSubtable *subtable_prime /* INOUT */,
|
||||
unsigned int *size /* OUT (accumulated) */) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
unsigned int offset, length, format;
|
||||
if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false);
|
||||
|
||||
auto* header_prime = subtable_prime->get_header();
|
||||
unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset;
|
||||
if (unlikely (!CBDT_internal::copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false);
|
||||
|
||||
return_trace (subtable_prime->add_offset (c, new_local_offset, size));
|
||||
}
|
||||
|
||||
bool add_offset (hb_serialize_context_t *c,
|
||||
unsigned int local_offset,
|
||||
unsigned int *size /* OUT (accumulated) */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
switch (u.header.indexFormat) {
|
||||
case 1: return_trace (u.format1.add_offset (c, local_offset, size));
|
||||
case 3: return_trace (u.format3.add_offset (c, local_offset, size));
|
||||
// TODO: Implement tables 2, 4, 5
|
||||
case 2: // Should be a no-op.
|
||||
case 4: case 5: // Handle sparse cases.
|
||||
default: return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const
|
||||
{
|
||||
switch (u.header.indexFormat) {
|
||||
|
@ -188,6 +318,25 @@ struct IndexSubtable
|
|||
}
|
||||
}
|
||||
|
||||
const IndexSubtableHeader* get_header() const
|
||||
{
|
||||
return &u.header;
|
||||
}
|
||||
|
||||
void populate_header (unsigned index_format,
|
||||
unsigned image_format,
|
||||
unsigned int image_data_offset,
|
||||
unsigned int *size)
|
||||
{
|
||||
u.header.indexFormat = index_format;
|
||||
u.header.imageFormat = image_format;
|
||||
u.header.imageDataOffset = image_data_offset;
|
||||
switch (u.header.indexFormat) {
|
||||
case 1: *size += IndexSubtableFormat1::min_size; break;
|
||||
case 3: *size += IndexSubtableFormat3::min_size; break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
union {
|
||||
IndexSubtableHeader header;
|
||||
|
@ -209,6 +358,127 @@ struct IndexSubtableRecord
|
|||
offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
|
||||
}
|
||||
|
||||
const IndexSubtable* get_subtable (const void *base) const
|
||||
{
|
||||
return &(base+offsetToSubtable);
|
||||
}
|
||||
|
||||
bool add_new_subtable (hb_subset_context_t* c,
|
||||
cblc_bitmap_size_subset_context_t *bitmap_size_context,
|
||||
IndexSubtableRecord *record,
|
||||
const hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> *lookup, /* IN */
|
||||
const void *base,
|
||||
unsigned int *start /* INOUT */) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
auto* subtable = c->serializer->start_embed<IndexSubtable> ();
|
||||
if (unlikely (!subtable)) return_trace (false);
|
||||
if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false);
|
||||
|
||||
auto* old_subtable = get_subtable (base);
|
||||
auto* old_header = old_subtable->get_header ();
|
||||
|
||||
subtable->populate_header (old_header->indexFormat,
|
||||
old_header->imageFormat,
|
||||
bitmap_size_context->cbdt_prime->length,
|
||||
&bitmap_size_context->size);
|
||||
|
||||
unsigned int num_glyphs = 0;
|
||||
bool early_exit = false;
|
||||
for (unsigned int i = *start; i < lookup->length; i++)
|
||||
{
|
||||
hb_codepoint_t new_gid = (*lookup)[i].first;
|
||||
const IndexSubtableRecord *next_record = (*lookup)[i].second;
|
||||
const IndexSubtable *next_subtable = next_record->get_subtable (base);
|
||||
auto* next_header = next_subtable->get_header ();
|
||||
if (next_header != old_header) {
|
||||
*start = i;
|
||||
early_exit = true;
|
||||
break;
|
||||
}
|
||||
unsigned int num_missing = record->add_glyph_for_subset (new_gid);
|
||||
if (unlikely (!subtable->fill_missing_glyphs (c->serializer,
|
||||
bitmap_size_context->cbdt_prime->length,
|
||||
num_missing,
|
||||
&bitmap_size_context->size,
|
||||
&num_glyphs)))
|
||||
return_trace (false);
|
||||
|
||||
hb_codepoint_t old_gid = 0;
|
||||
c->plan->old_gid_for_new_gid (new_gid, &old_gid);
|
||||
if (old_gid < next_record->firstGlyphIndex)
|
||||
return_trace (false);
|
||||
|
||||
unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex;
|
||||
if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer,
|
||||
old_idx,
|
||||
bitmap_size_context->cbdt,
|
||||
bitmap_size_context->cbdt_prime,
|
||||
subtable,
|
||||
&bitmap_size_context->size)))
|
||||
return_trace (false);
|
||||
num_glyphs += 1;
|
||||
}
|
||||
if (!early_exit)
|
||||
*start = lookup->length;
|
||||
if (unlikely (!subtable->finish_subtable (c->serializer,
|
||||
bitmap_size_context->cbdt_prime->length,
|
||||
num_glyphs,
|
||||
&bitmap_size_context->size)))
|
||||
return_trace (false);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool add_new_record (hb_subset_context_t *c,
|
||||
cblc_bitmap_size_subset_context_t *bitmap_size_context,
|
||||
const hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> *lookup, /* IN */
|
||||
const void *base,
|
||||
unsigned int *start, /* INOUT */
|
||||
hb_vector_t<IndexSubtableRecord>* records /* INOUT */) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto snap = c->serializer->snapshot ();
|
||||
unsigned int old_size = bitmap_size_context->size;
|
||||
unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length;
|
||||
|
||||
// Set to invalid state to indicate filling glyphs is not yet started.
|
||||
records->resize (records->length + 1);
|
||||
(*records)[records->length - 1].firstGlyphIndex = 1;
|
||||
(*records)[records->length - 1].lastGlyphIndex = 0;
|
||||
bitmap_size_context->size += IndexSubtableRecord::min_size;
|
||||
|
||||
c->serializer->push ();
|
||||
|
||||
if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start)))
|
||||
{
|
||||
c->serializer->pop_discard ();
|
||||
c->serializer->revert (snap);
|
||||
bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length);
|
||||
bitmap_size_context->size = old_size;
|
||||
records->resize (records->length - 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
bitmap_size_context->num_tables += 1;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
unsigned int add_glyph_for_subset (hb_codepoint_t gid) {
|
||||
if (firstGlyphIndex > lastGlyphIndex)
|
||||
{
|
||||
firstGlyphIndex = gid;
|
||||
lastGlyphIndex = gid;
|
||||
return 0;
|
||||
}
|
||||
// TODO maybe assert? this shouldn't occur.
|
||||
if (lastGlyphIndex > gid)
|
||||
return 0;
|
||||
unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1);
|
||||
lastGlyphIndex = gid;
|
||||
return num_missing;
|
||||
}
|
||||
|
||||
bool get_extents (hb_glyph_extents_t *extents,
|
||||
const void *base) const
|
||||
{
|
||||
|
@ -243,6 +513,70 @@ struct IndexSubtableArray
|
|||
return_trace (indexSubtablesZ.sanitize (c, count, this));
|
||||
}
|
||||
|
||||
void build_lookup (hb_subset_context_t *c,
|
||||
cblc_bitmap_size_subset_context_t *bitmap_size_context,
|
||||
hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> *lookup /* OUT */) const
|
||||
{
|
||||
bool start_glyph_is_set = false;
|
||||
for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++)
|
||||
{
|
||||
hb_codepoint_t old_gid;
|
||||
if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue;
|
||||
|
||||
const IndexSubtableRecord* record = find_table(old_gid, bitmap_size_context->num_tables);
|
||||
if (unlikely (!record)) continue;
|
||||
|
||||
// Don't add gaps to the lookup. The best way to determine if a glyph is a
|
||||
// gap is that it has no image data.
|
||||
unsigned int offset, length, format;
|
||||
if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue;
|
||||
|
||||
lookup->push (hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*> (new_gid, record));
|
||||
|
||||
if (!start_glyph_is_set)
|
||||
{
|
||||
bitmap_size_context->start_glyph = new_gid;
|
||||
start_glyph_is_set = true;
|
||||
}
|
||||
|
||||
bitmap_size_context->end_glyph = new_gid;
|
||||
}
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c,
|
||||
cblc_bitmap_size_subset_context_t *bitmap_size_context) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
auto* dst = c->serializer->start_embed<IndexSubtableArray> ();
|
||||
if (unlikely (!dst)) return_trace (false);
|
||||
|
||||
hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> lookup;
|
||||
build_lookup (c, bitmap_size_context, &lookup);
|
||||
|
||||
bitmap_size_context->size = 0;
|
||||
bitmap_size_context->num_tables = 0;
|
||||
hb_vector_t<IndexSubtableRecord> records;
|
||||
for (unsigned int start = 0; start < lookup.length;)
|
||||
{
|
||||
if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records)))
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
// Workaround to ensure offset ordering is from least to greatest when
|
||||
// resolving links.
|
||||
hb_vector_t<hb_serialize_context_t::objidx_t> objidxs;
|
||||
for (unsigned int i = 0; i < records.length; i++)
|
||||
objidxs.push (c->serializer->pop_pack());
|
||||
for (unsigned int i = 0; i < records.length; i++)
|
||||
{
|
||||
IndexSubtableRecord* record = c->serializer->embed (records[i]);
|
||||
if (unlikely (!record)) return_trace (false);
|
||||
c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i], dst);
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
public:
|
||||
const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
|
||||
{
|
||||
|
@ -282,6 +616,42 @@ struct BitmapSizeTable
|
|||
return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
|
||||
}
|
||||
|
||||
bool subset(hb_subset_context_t *c,
|
||||
const void *src_base,
|
||||
const void *dst_base,
|
||||
const char *cbdt,
|
||||
hb_vector_t<char> *cbdt_prime /* INOUT */) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto* out_table = c->serializer->embed (this);
|
||||
if (unlikely (!out_table)) return_trace (false);
|
||||
|
||||
cblc_bitmap_size_subset_context_t bitmap_size_context;
|
||||
bitmap_size_context.cbdt = cbdt;
|
||||
bitmap_size_context.cbdt_prime = cbdt_prime;
|
||||
bitmap_size_context.size = indexTablesSize;
|
||||
bitmap_size_context.num_tables = numberOfIndexSubtables;
|
||||
bitmap_size_context.start_glyph = 1;
|
||||
bitmap_size_context.end_glyph = 0;
|
||||
|
||||
if (!out_table->indexSubtableArrayOffset.serialize_subset(c,
|
||||
indexSubtableArrayOffset,
|
||||
src_base,
|
||||
dst_base,
|
||||
&bitmap_size_context))
|
||||
return_trace (false);
|
||||
if (!bitmap_size_context.size ||
|
||||
!bitmap_size_context.num_tables ||
|
||||
bitmap_size_context.start_glyph > bitmap_size_context.end_glyph)
|
||||
return_trace (false);
|
||||
|
||||
out_table->indexTablesSize = bitmap_size_context.size;
|
||||
out_table->numberOfIndexSubtables = bitmap_size_context.num_tables;
|
||||
out_table->startGlyphIndex = bitmap_size_context.start_glyph;
|
||||
out_table->endGlyphIndex = bitmap_size_context.end_glyph;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
LNNOffsetTo<IndexSubtableArray>
|
||||
indexSubtableArrayOffset;
|
||||
|
@ -342,6 +712,45 @@ struct CBLC
|
|||
sizeTables.sanitize (c, this));
|
||||
}
|
||||
|
||||
static bool sink_cbdt(hb_subset_context_t *c,
|
||||
hb_vector_t<char>* cbdt_prime)
|
||||
{
|
||||
hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ,
|
||||
cbdt_prime->length,
|
||||
HB_MEMORY_MODE_WRITABLE,
|
||||
cbdt_prime->arrayZ,
|
||||
free);
|
||||
cbdt_prime->init (); // Leak arrayZ to the blob.
|
||||
bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob);
|
||||
hb_blob_destroy (cbdt_prime_blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subset_size_table (hb_subset_context_t *c,
|
||||
const BitmapSizeTable& table,
|
||||
const char *cbdt /* IN */,
|
||||
CBLC *cblc_prime /* INOUT */,
|
||||
hb_vector_t<char> *cbdt_prime /* INOUT */) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
cblc_prime->sizeTables.len++;
|
||||
|
||||
auto snap = c->serializer->snapshot ();
|
||||
auto cbdt_prime_len = cbdt_prime->length;
|
||||
|
||||
if (!table.subset (c, this, cblc_prime, cbdt, cbdt_prime))
|
||||
{
|
||||
cblc_prime->sizeTables.len--;
|
||||
c->serializer->revert (snap);
|
||||
cbdt_prime->shrink (cbdt_prime_len);
|
||||
return_trace (false);
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
// Implemented in cc file as it depends on definition of CBDT.
|
||||
HB_INTERNAL bool subset (hb_subset_context_t *c) const;
|
||||
|
||||
protected:
|
||||
const BitmapSizeTable &choose_strike (hb_font_t *font) const
|
||||
{
|
||||
|
|
|
@ -54,6 +54,18 @@ struct head
|
|||
return 16 <= upem && upem <= 16384 ? upem : 1000;
|
||||
}
|
||||
|
||||
bool serialize (hb_serialize_context_t *c) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
return_trace ((bool) c->embed(this));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
return_trace (serialize(c->serializer));
|
||||
}
|
||||
|
||||
enum mac_style_flag_t {
|
||||
BOLD = 1u<<0,
|
||||
ITALIC = 1u<<1,
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#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"
|
||||
|
@ -153,6 +154,53 @@ _subset (hb_subset_plan_t *plan)
|
|||
}
|
||||
|
||||
|
||||
static bool
|
||||
_is_table_present (hb_face_t *source,
|
||||
hb_tag_t tag)
|
||||
{
|
||||
unsigned tables = hb_face_get_table_tags (source, 0, nullptr, nullptr);
|
||||
hb_vector_t<uint32_t> tags;
|
||||
tags.resize (tables);
|
||||
hb_face_get_table_tags (source, 0, &tables, tags.arrayZ);
|
||||
for (unsigned int i = 0; i < tables; i++) {
|
||||
if (tags[i] == tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
|
||||
{
|
||||
if (plan->drop_tables->has (tag))
|
||||
return true;
|
||||
|
||||
switch (tag) {
|
||||
case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
|
||||
case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */
|
||||
case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */
|
||||
case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */
|
||||
case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */
|
||||
case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
|
||||
return plan->drop_hints;
|
||||
|
||||
#ifdef HB_NO_SUBSET_LAYOUT
|
||||
// Drop Layout Tables if requested.
|
||||
case HB_OT_TAG_GDEF:
|
||||
case HB_OT_TAG_GPOS:
|
||||
case HB_OT_TAG_GSUB:
|
||||
case HB_TAG ('m', 'o', 'r', 'x'):
|
||||
case HB_TAG ('m', 'o', 'r', 't'):
|
||||
case HB_TAG ('k', 'e', 'r', 'x'):
|
||||
case HB_TAG ('k', 'e', 'r', 'n'):
|
||||
return true;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
_subset_table (hb_subset_plan_t *plan,
|
||||
hb_tag_t tag)
|
||||
|
@ -169,11 +217,15 @@ _subset_table (hb_subset_plan_t *plan,
|
|||
case HB_OT_TAG_name:
|
||||
result = _subset2<const OT::name> (plan);
|
||||
break;
|
||||
case HB_OT_TAG_head:
|
||||
// TODO that won't work well if there is no glyf
|
||||
DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
|
||||
result = true;
|
||||
case HB_OT_TAG_head: {
|
||||
if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf))
|
||||
{
|
||||
DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
|
||||
return true;
|
||||
}
|
||||
result = _subset2<const OT::head> (plan);
|
||||
break;
|
||||
}
|
||||
case HB_OT_TAG_hhea:
|
||||
DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx");
|
||||
return true;
|
||||
|
@ -207,6 +259,12 @@ _subset_table (hb_subset_plan_t *plan,
|
|||
case HB_OT_TAG_COLR:
|
||||
result = _subset2<const OT::COLR> (plan);
|
||||
break;
|
||||
case HB_OT_TAG_CBLC:
|
||||
result = _subset2<const OT::CBLC> (plan);
|
||||
break;
|
||||
case HB_OT_TAG_CBDT:
|
||||
DEBUG_MSG(SUBSET, nullptr, "skip CBDT handled by CBLC");
|
||||
return true;
|
||||
|
||||
#ifndef HB_NO_SUBSET_CFF
|
||||
case HB_OT_TAG_cff1:
|
||||
|
@ -254,38 +312,6 @@ _subset_table (hb_subset_plan_t *plan,
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
|
||||
{
|
||||
if (plan->drop_tables->has (tag))
|
||||
return true;
|
||||
|
||||
switch (tag) {
|
||||
case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
|
||||
case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */
|
||||
case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */
|
||||
case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */
|
||||
case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */
|
||||
case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
|
||||
return plan->drop_hints;
|
||||
|
||||
#ifdef HB_NO_SUBSET_LAYOUT
|
||||
// Drop Layout Tables if requested.
|
||||
case HB_OT_TAG_GDEF:
|
||||
case HB_OT_TAG_GPOS:
|
||||
case HB_OT_TAG_GSUB:
|
||||
case HB_TAG ('m', 'o', 'r', 'x'):
|
||||
case HB_TAG ('m', 'o', 'r', 't'):
|
||||
case HB_TAG ('k', 'e', 'r', 'x'):
|
||||
case HB_TAG ('k', 'e', 'r', 'n'):
|
||||
return true;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hb_subset:
|
||||
* @source: font face data to be subset.
|
||||
|
|
|
@ -59,6 +59,7 @@ TEST_PROGS = \
|
|||
test-subset-sbix \
|
||||
test-subset-gpos \
|
||||
test-subset-colr \
|
||||
test-subset-cbdt \
|
||||
test-unicode \
|
||||
test-version \
|
||||
test-subset-nameids \
|
||||
|
@ -79,6 +80,7 @@ test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
|||
test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_sbix_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_cbdt_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_nameids_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_gpos_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_colr_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright © 2020 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Calder Kitagawa
|
||||
*/
|
||||
|
||||
#include "hb-test.h"
|
||||
#include "hb-subset-test.h"
|
||||
|
||||
/* Unit tests for CBDT/CBLC subsetting */
|
||||
|
||||
static void
|
||||
test_subset_cbdt_noop (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x38);
|
||||
hb_set_add (codepoints, 0x39);
|
||||
hb_set_add (codepoints, 0xAE);
|
||||
hb_set_add (codepoints, 0x2049);
|
||||
hb_set_add (codepoints, 0x20E3);
|
||||
face_subset = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
|
||||
hb_set_destroy (codepoints);
|
||||
|
||||
hb_subset_test_check (face, face_subset, HB_TAG ('C','B','L','C'));
|
||||
hb_subset_test_check (face, face_subset, HB_TAG ('C','B','D','T'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_cbdt_keep_one (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.default.39.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x39);
|
||||
face_subset = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
|
||||
hb_set_destroy (codepoints);
|
||||
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','L','C'));
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','D','T'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_cbdt_keep_one_last_subtable (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.default.2049.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x2049);
|
||||
face_subset = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
|
||||
hb_set_destroy (codepoints);
|
||||
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','L','C'));
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','D','T'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_cbdt_keep_multiple_subtables (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.multiple_size_tables.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.multiple_size_tables.default.38,AE,2049.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x38);
|
||||
hb_set_add (codepoints, 0xAE);
|
||||
hb_set_add (codepoints, 0x2049);
|
||||
face_subset = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
|
||||
hb_set_destroy (codepoints);
|
||||
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','L','C'));
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','D','T'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_cbdt_index_format_3 (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.index_format3.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/NotoColorEmoji.subset.index_format3.default.38,AE,2049.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x38);
|
||||
hb_set_add (codepoints, 0xAE);
|
||||
hb_set_add (codepoints, 0x2049);
|
||||
face_subset = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
|
||||
hb_set_destroy (codepoints);
|
||||
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','L','C'));
|
||||
hb_subset_test_check (face_expected, face_subset, HB_TAG ('C','B','D','T'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
// TODO: add support/tests for index formats 2,4,5 (image formats are treated as
|
||||
// opaque blobs when subsetting so don't need to be tested separately).
|
||||
// TODO: add a test that keeps no codepoints.
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
hb_test_init (&argc, &argv);
|
||||
|
||||
hb_test_add (test_subset_cbdt_noop);
|
||||
hb_test_add (test_subset_cbdt_keep_one);
|
||||
hb_test_add (test_subset_cbdt_keep_one_last_subtable);
|
||||
// The following use manually crafted expectation files as they are not
|
||||
// binary compatible with FontTools.
|
||||
hb_test_add (test_subset_cbdt_keep_multiple_subtables);
|
||||
// Can use FontTools after https://github.com/fonttools/fonttools/issues/1817
|
||||
// is resolved.
|
||||
hb_test_add (test_subset_cbdt_index_format_3);
|
||||
|
||||
return hb_test_run();
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -20,6 +20,7 @@ EXTRA_DIST += \
|
|||
expected/cmap14 \
|
||||
expected/sbix \
|
||||
expected/colr \
|
||||
expected/cbdt \
|
||||
fonts \
|
||||
profiles \
|
||||
$(NULL)
|
||||
|
|
|
@ -12,6 +12,7 @@ TESTS = \
|
|||
tests/cmap14.tests \
|
||||
tests/sbix.tests \
|
||||
tests/colr.tests \
|
||||
tests/cbdt.tests \
|
||||
$(NULL)
|
||||
|
||||
XFAIL_TESTS = \
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue