Merge pull request #2132 from ckitagawa-work/subset_colr
[subset] Add COLR support
This commit is contained in:
commit
777ba47b50
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
* Copyright © 2020 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
@ -20,6 +21,8 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef HB_OT_COLOR_COLR_TABLE_HH
|
||||
@ -47,7 +50,7 @@ struct LayerRecord
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
protected:
|
||||
public:
|
||||
HBGlyphID glyphId; /* Glyph ID of layer glyph */
|
||||
Index colorIdx; /* Index value to use with a
|
||||
* selected color palette.
|
||||
@ -112,6 +115,37 @@ struct COLR
|
||||
return glyph_layers.length;
|
||||
}
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t () {}
|
||||
~accelerator_t () { fini (); }
|
||||
|
||||
void init (hb_face_t *face)
|
||||
{ colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
|
||||
|
||||
void fini () { this->colr.destroy (); }
|
||||
|
||||
bool is_valid () { return colr.get_blob ()->length; }
|
||||
|
||||
void closure_glyphs (hb_codepoint_t glyph,
|
||||
hb_set_t *related_ids /* OUT */) const
|
||||
{ colr->closure_glyphs (glyph, related_ids); }
|
||||
|
||||
private:
|
||||
hb_blob_ptr_t<COLR> colr;
|
||||
};
|
||||
|
||||
void closure_glyphs (hb_codepoint_t glyph,
|
||||
hb_set_t *related_ids /* OUT */) const
|
||||
{
|
||||
const BaseGlyphRecord *record = get_base_glyph_record (glyph);
|
||||
if (!record) return;
|
||||
|
||||
hb_array_t<const LayerRecord> glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, record->numLayers);
|
||||
if (!glyph_layers.length) return;
|
||||
related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
@ -120,6 +154,111 @@ struct COLR
|
||||
(this+layersZ).sanitize (c, numLayers)));
|
||||
}
|
||||
|
||||
template<typename BaseIterator, typename LayerIterator,
|
||||
hb_requires (hb_is_iterator (BaseIterator)),
|
||||
hb_requires (hb_is_iterator (LayerIterator))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
unsigned version,
|
||||
BaseIterator base_it,
|
||||
LayerIterator layer_it)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (base_it.len () != layer_it.len ()))
|
||||
return_trace (false);
|
||||
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
this->version = version;
|
||||
numLayers = 0;
|
||||
numBaseGlyphs = base_it.len ();
|
||||
baseGlyphsZ = COLR::min_size;
|
||||
layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size;
|
||||
|
||||
for (const hb_item_type<BaseIterator>& _ : + base_it.iter ())
|
||||
{
|
||||
auto* record = c->embed(_);
|
||||
if (unlikely (!record)) return_trace (false);
|
||||
record->firstLayerIdx = numLayers;
|
||||
numLayers += record->numLayers;
|
||||
}
|
||||
|
||||
for (const hb_item_type<LayerIterator>& _ : + layer_it.iter ())
|
||||
_.as_array ().copy (c);
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const
|
||||
{
|
||||
if ((unsigned int) gid == 0) // Ignore notdef.
|
||||
return nullptr;
|
||||
const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid);
|
||||
if ((record && (hb_codepoint_t) record->glyphId != gid))
|
||||
record = nullptr;
|
||||
return record;
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
|
||||
|
||||
auto base_it =
|
||||
+ hb_range (c->plan->num_output_glyphs ())
|
||||
| hb_map_retains_sorting ([&](hb_codepoint_t new_gid)
|
||||
{
|
||||
hb_codepoint_t old_gid = reverse_glyph_map.get(new_gid);
|
||||
const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
|
||||
|
||||
BaseGlyphRecord new_record;
|
||||
if (unlikely (!old_record))
|
||||
return hb_pair_t<bool, BaseGlyphRecord> (false, new_record);
|
||||
|
||||
new_record.glyphId = new_gid;
|
||||
new_record.numLayers = old_record->numLayers;
|
||||
return hb_pair_t<bool, BaseGlyphRecord> (true, new_record);
|
||||
})
|
||||
| hb_filter (hb_first)
|
||||
| hb_map_retains_sorting (hb_second)
|
||||
;
|
||||
|
||||
auto layer_it =
|
||||
+ hb_range (c->plan->num_output_glyphs ())
|
||||
| hb_map (reverse_glyph_map)
|
||||
| hb_map_retains_sorting ([&](hb_codepoint_t old_gid)
|
||||
{
|
||||
const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid);
|
||||
hb_vector_t<LayerRecord> out_layers;
|
||||
|
||||
if (unlikely (!old_record ||
|
||||
old_record->firstLayerIdx >= numLayers ||
|
||||
old_record->firstLayerIdx + old_record->numLayers > numLayers))
|
||||
return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
|
||||
|
||||
hb_array_t<const LayerRecord> layers = (this+layersZ).as_array (numLayers).sub_array(old_record->firstLayerIdx,
|
||||
old_record->numLayers);
|
||||
out_layers.resize (layers.length);
|
||||
for (unsigned int i = 0; i < layers.length; i++) {
|
||||
out_layers[i] = layers[i];
|
||||
hb_codepoint_t new_gid = 0;
|
||||
if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
|
||||
return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
|
||||
out_layers[i].glyphId = new_gid;
|
||||
}
|
||||
|
||||
return hb_pair_t<bool, hb_vector_t<LayerRecord>> (true, out_layers);
|
||||
})
|
||||
| hb_filter (hb_first)
|
||||
| hb_map_retains_sorting (hb_second)
|
||||
;
|
||||
|
||||
if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ()))
|
||||
return_trace (false);
|
||||
|
||||
COLR *colr_prime = c->serializer->start_embed<COLR> ();
|
||||
return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Table version number (starts at 0). */
|
||||
HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "hb-ot-cmap-table.hh"
|
||||
#include "hb-ot-glyf-table.hh"
|
||||
#include "hb-ot-cff1-table.hh"
|
||||
#include "hb-ot-color-colr-table.hh"
|
||||
#include "hb-ot-var-fvar-table.hh"
|
||||
#include "hb-ot-stat-table.hh"
|
||||
|
||||
@ -156,11 +157,13 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
|
||||
#ifndef HB_NO_SUBSET_CFF
|
||||
OT::cff1::accelerator_t cff;
|
||||
#endif
|
||||
OT::COLR::accelerator_t colr;
|
||||
cmap.init (plan->source);
|
||||
glyf.init (plan->source);
|
||||
#ifndef HB_NO_SUBSET_CFF
|
||||
cff.init (plan->source);
|
||||
#endif
|
||||
colr.init (plan->source);
|
||||
|
||||
plan->_glyphset_gsub->add (0); // Not-def
|
||||
hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain);
|
||||
@ -201,6 +204,8 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
|
||||
if (cff.is_valid ())
|
||||
_add_cff_seac_components (cff, gid, plan->_glyphset);
|
||||
#endif
|
||||
if (colr.is_valid ())
|
||||
colr.closure_glyphs (gid, plan->_glyphset);
|
||||
}
|
||||
|
||||
_remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
|
||||
|
@ -38,6 +38,7 @@
|
||||
#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"
|
||||
@ -203,6 +204,9 @@ _subset_table (hb_subset_plan_t *plan,
|
||||
case HB_OT_TAG_post:
|
||||
result = _subset2<const OT::post> (plan);
|
||||
break;
|
||||
case HB_OT_TAG_COLR:
|
||||
result = _subset2<const OT::COLR> (plan);
|
||||
break;
|
||||
|
||||
#ifndef HB_NO_SUBSET_CFF
|
||||
case HB_OT_TAG_cff1:
|
||||
|
@ -58,6 +58,7 @@ TEST_PROGS = \
|
||||
test-subset-vvar \
|
||||
test-subset-sbix \
|
||||
test-subset-gpos \
|
||||
test-subset-colr \
|
||||
test-unicode \
|
||||
test-version \
|
||||
test-subset-nameids \
|
||||
@ -80,6 +81,7 @@ test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
|
||||
test_subset_sbix_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
|
||||
|
||||
test_unicode_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
|
BIN
test/api/fonts/TwemojiMozilla.subset.default.32,3299.ttf
Normal file
BIN
test/api/fonts/TwemojiMozilla.subset.default.32,3299.ttf
Normal file
Binary file not shown.
BIN
test/api/fonts/TwemojiMozilla.subset.default.32.ttf
Normal file
BIN
test/api/fonts/TwemojiMozilla.subset.default.32.ttf
Normal file
Binary file not shown.
BIN
test/api/fonts/TwemojiMozilla.subset.default.3297.ttf
Normal file
BIN
test/api/fonts/TwemojiMozilla.subset.default.3297.ttf
Normal file
Binary file not shown.
BIN
test/api/fonts/TwemojiMozilla.subset.ttf
Normal file
BIN
test/api/fonts/TwemojiMozilla.subset.ttf
Normal file
Binary file not shown.
@ -99,7 +99,10 @@ hb_subset_test_check (hb_face_t *expected,
|
||||
expected_blob = hb_face_reference_table (expected, table);
|
||||
actual_blob = hb_face_reference_table (actual, table);
|
||||
fprintf(stderr, "comparing %c%c%c%c, expected %d bytes, actual %d bytes\n", HB_UNTAG(table), hb_blob_get_length(expected_blob), hb_blob_get_length (actual_blob));
|
||||
hb_test_assert_blobs_equal (expected_blob, actual_blob);
|
||||
|
||||
if (hb_blob_get_length (expected_blob) != 0 ||
|
||||
hb_blob_get_length (actual_blob) != 0)
|
||||
hb_test_assert_blobs_equal (expected_blob, actual_blob);
|
||||
hb_blob_destroy (expected_blob);
|
||||
hb_blob_destroy (actual_blob);
|
||||
}
|
||||
|
120
test/api/test-subset-colr.c
Normal file
120
test/api/test-subset-colr.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 COLR subsetting */
|
||||
|
||||
static void
|
||||
test_subset_colr_noop (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, '2');
|
||||
hb_set_add (codepoints, 0x3297);
|
||||
hb_set_add (codepoints, 0x3299);
|
||||
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','O','L','R'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_colr_keep_one_colr_glyph (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.default.3297.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, 0x3297);
|
||||
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','O','L','R'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_colr_keep_mixed_glyph (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.default.32,3299.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, '2');
|
||||
hb_set_add (codepoints, 0x3299);
|
||||
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','O','L','R'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
static void
|
||||
test_subset_colr_keep_no_colr_glyph (void)
|
||||
{
|
||||
hb_face_t *face = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.ttf");
|
||||
hb_face_t *face_expected = hb_test_open_font_file ("fonts/TwemojiMozilla.subset.default.32.ttf");
|
||||
|
||||
hb_set_t *codepoints = hb_set_create ();
|
||||
hb_face_t *face_subset;
|
||||
hb_set_add (codepoints, '2');
|
||||
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','O','L','R'));
|
||||
|
||||
hb_face_destroy (face_subset);
|
||||
hb_face_destroy (face_expected);
|
||||
hb_face_destroy (face);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
hb_test_init (&argc, &argv);
|
||||
|
||||
hb_test_add (test_subset_colr_noop);
|
||||
hb_test_add (test_subset_colr_keep_one_colr_glyph);
|
||||
hb_test_add (test_subset_colr_keep_mixed_glyph);
|
||||
hb_test_add (test_subset_colr_keep_no_colr_glyph);
|
||||
|
||||
return hb_test_run();
|
||||
}
|
BIN
test/fuzzing/fonts/TwemojiMozilla.subset.ttf
Normal file
BIN
test/fuzzing/fonts/TwemojiMozilla.subset.ttf
Normal file
Binary file not shown.
@ -19,6 +19,7 @@ EXTRA_DIST += \
|
||||
expected/layout.gsub6 \
|
||||
expected/cmap14 \
|
||||
expected/sbix \
|
||||
expected/colr \
|
||||
fonts \
|
||||
profiles \
|
||||
$(NULL)
|
||||
|
@ -11,6 +11,7 @@ TESTS = \
|
||||
tests/layout.gsub6.tests \
|
||||
tests/cmap14.tests \
|
||||
tests/sbix.tests \
|
||||
tests/colr.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.
BIN
test/subset/data/fonts/TwemojiMozilla.subset.ttf
Normal file
BIN
test/subset/data/fonts/TwemojiMozilla.subset.ttf
Normal file
Binary file not shown.
17
test/subset/data/tests/colr.tests
Normal file
17
test/subset/data/tests/colr.tests
Normal file
@ -0,0 +1,17 @@
|
||||
FONTS:
|
||||
TwemojiMozilla.subset.ttf
|
||||
|
||||
PROFILES:
|
||||
default.txt
|
||||
drop-hints.txt
|
||||
drop-hints-retain-gids.txt
|
||||
retain-gids.txt
|
||||
|
||||
SUBSETS:
|
||||
2
|
||||
㊗
|
||||
㊙
|
||||
2㊗
|
||||
2㊙
|
||||
㊗㊙
|
||||
2㊗㊙
|
Loading…
Reference in New Issue
Block a user